PageRenderTime 1028ms CodeModel.GetById 202ms app.highlight 511ms RepoModel.GetById 173ms app.codeStats 1ms

/tests/regressiontests/templates/tests.py

https://code.google.com/p/mango-py/
Python | 1675 lines | 1454 code | 122 blank | 99 comment | 188 complexity | a92aba6f23007bf4edf6ab6112873c86 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1# -*- coding: utf-8 -*-
  2from django.conf import settings
  3
  4if __name__ == '__main__':
  5    # When running this file in isolation, we need to set up the configuration
  6    # before importing 'template'.
  7    settings.configure()
  8
  9from datetime import datetime, timedelta
 10import time
 11import os
 12import sys
 13import traceback
 14
 15from django import template
 16from django.template import base as template_base
 17from django.core import urlresolvers
 18from django.template import loader
 19from django.template.loaders import app_directories, filesystem, cached
 20from django.test.utils import setup_test_template_loader,\
 21    restore_template_loaders
 22from django.utils import unittest
 23from django.utils.translation import activate, deactivate, ugettext as _
 24from django.utils.safestring import mark_safe
 25from django.utils.tzinfo import LocalTimezone
 26
 27from context import ContextTests
 28from custom import CustomTagTests, CustomFilterTests
 29from parser import ParserTests
 30from unicode import UnicodeTests
 31from nodelist import NodelistTest
 32from smartif import *
 33from response import *
 34
 35try:
 36    from loaders import *
 37except ImportError:
 38    pass # If setuptools isn't installed, that's fine. Just move on.
 39
 40import filters
 41
 42#################################
 43# Custom template tag for tests #
 44#################################
 45
 46register = template.Library()
 47
 48class EchoNode(template.Node):
 49    def __init__(self, contents):
 50        self.contents = contents
 51
 52    def render(self, context):
 53        return " ".join(self.contents)
 54
 55def do_echo(parser, token):
 56    return EchoNode(token.contents.split()[1:])
 57
 58def do_upper(value):
 59    return value.upper()
 60
 61register.tag("echo", do_echo)
 62register.tag("other_echo", do_echo)
 63register.filter("upper", do_upper)
 64
 65template.libraries['testtags'] = register
 66
 67#####################################
 68# Helper objects for template tests #
 69#####################################
 70
 71class SomeException(Exception):
 72    silent_variable_failure = True
 73
 74class SomeOtherException(Exception):
 75    pass
 76
 77class ContextStackException(Exception):
 78    pass
 79
 80class SomeClass:
 81    def __init__(self):
 82        self.otherclass = OtherClass()
 83
 84    def method(self):
 85        return "SomeClass.method"
 86
 87    def method2(self, o):
 88        return o
 89
 90    def method3(self):
 91        raise SomeException
 92
 93    def method4(self):
 94        raise SomeOtherException
 95
 96    def __getitem__(self, key):
 97        if key == 'silent_fail_key':
 98            raise SomeException
 99        elif key == 'noisy_fail_key':
100            raise SomeOtherException
101        raise KeyError
102
103    def silent_fail_attribute(self):
104        raise SomeException
105    silent_fail_attribute = property(silent_fail_attribute)
106
107    def noisy_fail_attribute(self):
108        raise SomeOtherException
109    noisy_fail_attribute = property(noisy_fail_attribute)
110
111class OtherClass:
112    def method(self):
113        return "OtherClass.method"
114
115class TestObj(object):
116    def is_true(self):
117        return True
118
119    def is_false(self):
120        return False
121
122    def is_bad(self):
123        time.sleep(0.3)
124        return True
125
126class SilentGetItemClass(object):
127    def __getitem__(self, key):
128        raise SomeException
129
130class SilentAttrClass(object):
131    def b(self):
132        raise SomeException
133    b = property(b)
134
135class UTF8Class:
136    "Class whose __str__ returns non-ASCII data"
137    def __str__(self):
138        return u'Š??Ž?žš?'.encode('utf-8')
139
140class Templates(unittest.TestCase):
141    def setUp(self):
142        self.old_static_url = settings.STATIC_URL
143        self.old_media_url = settings.MEDIA_URL
144        settings.STATIC_URL = u"/static/"
145        settings.MEDIA_URL = u"/media/"
146
147    def tearDown(self):
148        settings.STATIC_URL = self.old_static_url
149        settings.MEDIA_URL = self.old_media_url
150
151    def test_loaders_security(self):
152        ad_loader = app_directories.Loader()
153        fs_loader = filesystem.Loader()
154        def test_template_sources(path, template_dirs, expected_sources):
155            if isinstance(expected_sources, list):
156                # Fix expected sources so they are normcased and abspathed
157                expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
158            # Test the two loaders (app_directores and filesystem).
159            func1 = lambda p, t: list(ad_loader.get_template_sources(p, t))
160            func2 = lambda p, t: list(fs_loader.get_template_sources(p, t))
161            for func in (func1, func2):
162                if isinstance(expected_sources, list):
163                    self.assertEqual(func(path, template_dirs), expected_sources)
164                else:
165                    self.assertRaises(expected_sources, func, path, template_dirs)
166
167        template_dirs = ['/dir1', '/dir2']
168        test_template_sources('index.html', template_dirs,
169                              ['/dir1/index.html', '/dir2/index.html'])
170        test_template_sources('/etc/passwd', template_dirs, [])
171        test_template_sources('etc/passwd', template_dirs,
172                              ['/dir1/etc/passwd', '/dir2/etc/passwd'])
173        test_template_sources('../etc/passwd', template_dirs, [])
174        test_template_sources('../../../etc/passwd', template_dirs, [])
175        test_template_sources('/dir1/index.html', template_dirs,
176                              ['/dir1/index.html'])
177        test_template_sources('../dir2/index.html', template_dirs,
178                              ['/dir2/index.html'])
179        test_template_sources('/dir1blah', template_dirs, [])
180        test_template_sources('../dir1blah', template_dirs, [])
181
182        # UTF-8 bytestrings are permitted.
183        test_template_sources('\xc3\x85ngstr\xc3\xb6m', template_dirs,
184                              [u'/dir1/Ĺngström', u'/dir2/Ĺngström'])
185        # Unicode strings are permitted.
186        test_template_sources(u'Ĺngström', template_dirs,
187                              [u'/dir1/Ĺngström', u'/dir2/Ĺngström'])
188        test_template_sources(u'Ĺngström', ['/Straße'], [u'/Straße/Ĺngström'])
189        test_template_sources('\xc3\x85ngstr\xc3\xb6m', ['/Straße'],
190                              [u'/Straße/Ĺngström'])
191        # Invalid UTF-8 encoding in bytestrings is not. Should raise a
192        # semi-useful error message.
193        test_template_sources('\xc3\xc3', template_dirs, UnicodeDecodeError)
194
195        # Case insensitive tests (for win32). Not run unless we're on
196        # a case insensitive operating system.
197        if os.path.normcase('/TEST') == os.path.normpath('/test'):
198            template_dirs = ['/dir1', '/DIR2']
199            test_template_sources('index.html', template_dirs,
200                                  ['/dir1/index.html', '/dir2/index.html'])
201            test_template_sources('/DIR1/index.HTML', template_dirs,
202                                  ['/dir1/index.html'])
203
204    def test_loader_debug_origin(self):
205        # Turn TEMPLATE_DEBUG on, so that the origin file name will be kept with
206        # the compiled templates.
207        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
208        old_loaders = loader.template_source_loaders
209
210        try:
211            loader.template_source_loaders = (filesystem.Loader(),)
212
213            # We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
214            # point to a directory containing a 404.html file. Also that
215            # the file system and app directories loaders both inherit the
216            # load_template method from the BaseLoader class, so we only need
217            # to test one of them.
218            load_name = '404.html'
219            template = loader.get_template(load_name)
220            template_name = template.nodelist[0].source[0].name
221            self.assertTrue(template_name.endswith(load_name),
222                'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_name)
223
224            # Aso test the cached loader, since it overrides load_template
225            cache_loader = cached.Loader(('',))
226            cache_loader._cached_loaders = loader.template_source_loaders
227            loader.template_source_loaders = (cache_loader,)
228
229            template = loader.get_template(load_name)
230            template_name = template.nodelist[0].source[0].name
231            self.assertTrue(template_name.endswith(load_name),
232                'Template loaded through cached loader has incorrect name for debug page: %s' % template_name)
233
234            template = loader.get_template(load_name)
235            template_name = template.nodelist[0].source[0].name
236            self.assertTrue(template_name.endswith(load_name),
237                'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name)
238        finally:
239            loader.template_source_loaders = old_loaders
240            settings.TEMPLATE_DEBUG = old_td
241
242
243    def test_include_missing_template(self):
244        """
245        Tests that the correct template is identified as not existing
246        when {% include %} specifies a template that does not exist.
247        """
248
249        # TEMPLATE_DEBUG must be true, otherwise the exception raised
250        # during {% include %} processing will be suppressed.
251        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
252        old_loaders = loader.template_source_loaders
253
254        try:
255            # Test the base loader class via the app loader. load_template
256            # from base is used by all shipped loaders excepting cached,
257            # which has its own test.
258            loader.template_source_loaders = (app_directories.Loader(),)
259
260            load_name = 'test_include_error.html'
261            r = None
262            try:
263                tmpl = loader.select_template([load_name])
264                r = tmpl.render(template.Context({}))
265            except template.TemplateDoesNotExist, e:
266                settings.TEMPLATE_DEBUG = old_td
267                self.assertEqual(e.args[0], 'missing.html')
268            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
269        finally:
270            loader.template_source_loaders = old_loaders
271            settings.TEMPLATE_DEBUG = old_td
272
273
274    def test_extends_include_missing_baseloader(self):
275        """
276        Tests that the correct template is identified as not existing
277        when {% extends %} specifies a template that does exist, but
278        that template has an {% include %} of something that does not
279        exist. See #12787.
280        """
281
282        # TEMPLATE_DEBUG must be true, otherwise the exception raised
283        # during {% include %} processing will be suppressed.
284        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
285        old_loaders = loader.template_source_loaders
286
287        try:
288            # Test the base loader class via the app loader. load_template
289            # from base is used by all shipped loaders excepting cached,
290            # which has its own test.
291            loader.template_source_loaders = (app_directories.Loader(),)
292
293            load_name = 'test_extends_error.html'
294            tmpl = loader.get_template(load_name)
295            r = None
296            try:
297                r = tmpl.render(template.Context({}))
298            except template.TemplateSyntaxError, e:
299                settings.TEMPLATE_DEBUG = old_td
300                self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
301            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
302        finally:
303            loader.template_source_loaders = old_loaders
304            settings.TEMPLATE_DEBUG = old_td
305
306    def test_extends_include_missing_cachedloader(self):
307        """
308        Same as test_extends_include_missing_baseloader, only tests
309        behavior of the cached loader instead of BaseLoader.
310        """
311
312        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
313        old_loaders = loader.template_source_loaders
314
315        try:
316            cache_loader = cached.Loader(('',))
317            cache_loader._cached_loaders = (app_directories.Loader(),)
318            loader.template_source_loaders = (cache_loader,)
319
320            load_name = 'test_extends_error.html'
321            tmpl = loader.get_template(load_name)
322            r = None
323            try:
324                r = tmpl.render(template.Context({}))
325            except template.TemplateSyntaxError, e:
326                self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
327            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
328
329            # For the cached loader, repeat the test, to ensure the first attempt did not cache a
330            # result that behaves incorrectly on subsequent attempts.
331            tmpl = loader.get_template(load_name)
332            try:
333                tmpl.render(template.Context({}))
334            except template.TemplateSyntaxError, e:
335                self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
336            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
337        finally:
338            loader.template_source_loaders = old_loaders
339            settings.TEMPLATE_DEBUG = old_td
340
341    def test_token_smart_split(self):
342        # Regression test for #7027
343        token = template.Token(template.TOKEN_BLOCK, 'sometag _("Page not found") value|yesno:_("yes,no")')
344        split = token.split_contents()
345        self.assertEqual(split, ["sometag", '_("Page not found")', 'value|yesno:_("yes,no")'])
346
347    def test_url_reverse_no_settings_module(self):
348        # Regression test for #9005
349        from django.template import Template, Context, TemplateSyntaxError
350
351        old_settings_module = settings.SETTINGS_MODULE
352        old_template_debug = settings.TEMPLATE_DEBUG
353
354        settings.SETTINGS_MODULE = None
355        settings.TEMPLATE_DEBUG = True
356
357        t = Template('{% url will_not_match %}')
358        c = Context()
359        try:
360            rendered = t.render(c)
361        except TemplateSyntaxError, e:
362            # Assert that we are getting the template syntax error and not the
363            # string encoding error.
364            self.assertEqual(e.args[0], "Caught NoReverseMatch while rendering: Reverse for 'will_not_match' with arguments '()' and keyword arguments '{}' not found.")
365
366        settings.SETTINGS_MODULE = old_settings_module
367        settings.TEMPLATE_DEBUG = old_template_debug
368
369    def test_invalid_block_suggestion(self):
370        # See #7876
371        from django.template import Template, TemplateSyntaxError
372        try:
373            t = Template("{% if 1 %}lala{% endblock %}{% endif %}")
374        except TemplateSyntaxError, e:
375            self.assertEqual(e.args[0], "Invalid block tag: 'endblock', expected 'else' or 'endif'")
376
377    def test_templates(self):
378        template_tests = self.get_template_tests()
379        filter_tests = filters.get_filter_tests()
380
381        # Quickly check that we aren't accidentally using a name in both
382        # template and filter tests.
383        overlapping_names = [name for name in filter_tests if name in template_tests]
384        assert not overlapping_names, 'Duplicate test name(s): %s' % ', '.join(overlapping_names)
385
386        template_tests.update(filter_tests)
387
388        # Register our custom template loader.
389        def test_template_loader(template_name, template_dirs=None):
390            "A custom template loader that loads the unit-test templates."
391            try:
392                return (template_tests[template_name][0] , "test:%s" % template_name)
393            except KeyError:
394                raise template.TemplateDoesNotExist(template_name)
395
396        cache_loader = cached.Loader(('test_template_loader',))
397        cache_loader._cached_loaders = (test_template_loader,)
398
399        old_template_loaders = loader.template_source_loaders
400        loader.template_source_loaders = [cache_loader]
401
402        failures = []
403        tests = template_tests.items()
404        tests.sort()
405
406        # Turn TEMPLATE_DEBUG off, because tests assume that.
407        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
408
409        # Set TEMPLATE_STRING_IF_INVALID to a known string.
410        old_invalid = settings.TEMPLATE_STRING_IF_INVALID
411        expected_invalid_str = 'INVALID'
412
413        #Set ALLOWED_INCLUDE_ROOTS so that ssi works.
414        old_allowed_include_roots = settings.ALLOWED_INCLUDE_ROOTS
415        settings.ALLOWED_INCLUDE_ROOTS = os.path.dirname(os.path.abspath(__file__))
416
417        # Warm the URL reversing cache. This ensures we don't pay the cost
418        # warming the cache during one of the tests.
419        urlresolvers.reverse('regressiontests.templates.views.client_action',
420                             kwargs={'id':0,'action':"update"})
421
422        for name, vals in tests:
423            if isinstance(vals[2], tuple):
424                normal_string_result = vals[2][0]
425                invalid_string_result = vals[2][1]
426
427                if isinstance(invalid_string_result, tuple):
428                    expected_invalid_str = 'INVALID %s'
429                    invalid_string_result = invalid_string_result[0] % invalid_string_result[1]
430                    template_base.invalid_var_format_string = True
431
432                try:
433                    template_debug_result = vals[2][2]
434                except IndexError:
435                    template_debug_result = normal_string_result
436
437            else:
438                normal_string_result = vals[2]
439                invalid_string_result = vals[2]
440                template_debug_result = vals[2]
441
442            if 'LANGUAGE_CODE' in vals[1]:
443                activate(vals[1]['LANGUAGE_CODE'])
444            else:
445                activate('en-us')
446
447            for invalid_str, template_debug, result in [
448                    ('', False, normal_string_result),
449                    (expected_invalid_str, False, invalid_string_result),
450                    ('', True, template_debug_result)
451                ]:
452                settings.TEMPLATE_STRING_IF_INVALID = invalid_str
453                settings.TEMPLATE_DEBUG = template_debug
454                for is_cached in (False, True):
455                    try:
456                        start = datetime.now()
457                        test_template = loader.get_template(name)
458                        end = datetime.now()
459                        if end-start > timedelta(seconds=0.2):
460                            failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Took too long to parse test" % (is_cached, invalid_str, template_debug, name))
461
462                        start = datetime.now()
463                        output = self.render(test_template, vals)
464                        end = datetime.now()
465                        if end-start > timedelta(seconds=0.2):
466                            failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Took too long to render test" % (is_cached, invalid_str, template_debug, name))
467                    except ContextStackException:
468                        failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Context stack was left imbalanced" % (is_cached, invalid_str, template_debug, name))
469                        continue
470                    except Exception:
471                        exc_type, exc_value, exc_tb = sys.exc_info()
472                        if exc_type != result:
473                            print "CHECK", name, exc_type, result
474                            tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
475                            failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Got %s, exception: %s\n%s" % (is_cached, invalid_str, template_debug, name, exc_type, exc_value, tb))
476                        continue
477                    if output != result:
478                        failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s', TEMPLATE_DEBUG=%s): %s -- FAILED. Expected %r, got %r" % (is_cached, invalid_str, template_debug, name, result, output))
479                cache_loader.reset()
480
481            if 'LANGUAGE_CODE' in vals[1]:
482                deactivate()
483
484            if template_base.invalid_var_format_string:
485                expected_invalid_str = 'INVALID'
486                template_base.invalid_var_format_string = False
487
488        loader.template_source_loaders = old_template_loaders
489        deactivate()
490        settings.TEMPLATE_DEBUG = old_td
491        settings.TEMPLATE_STRING_IF_INVALID = old_invalid
492        settings.ALLOWED_INCLUDE_ROOTS = old_allowed_include_roots
493
494        self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
495            ('-'*70, ("\n%s\n" % ('-'*70)).join(failures)))
496
497    def render(self, test_template, vals):
498        context = template.Context(vals[1])
499        before_stack_size = len(context.dicts)
500        output = test_template.render(context)
501        if len(context.dicts) != before_stack_size:
502            raise ContextStackException
503        return output
504
505    def get_template_tests(self):
506        # SYNTAX --
507        # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class)
508        return {
509            ### BASIC SYNTAX ################################################
510
511            # Plain text should go through the template parser untouched
512            'basic-syntax01': ("something cool", {}, "something cool"),
513
514            # Variables should be replaced with their value in the current
515            # context
516            'basic-syntax02': ("{{ headline }}", {'headline':'Success'}, "Success"),
517
518            # More than one replacement variable is allowed in a template
519            'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
520
521            # Fail silently when a variable is not found in the current context
522            'basic-syntax04': ("as{{ missing }}df", {}, ("asdf","asINVALIDdf")),
523
524            # A variable may not contain more than one word
525            'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
526
527            # Raise TemplateSyntaxError for empty variable tags
528            'basic-syntax07': ("{{ }}",        {}, template.TemplateSyntaxError),
529            'basic-syntax08': ("{{        }}", {}, template.TemplateSyntaxError),
530
531            # Attribute syntax allows a template to call an object's attribute
532            'basic-syntax09': ("{{ var.method }}", {"var": SomeClass()}, "SomeClass.method"),
533
534            # Multiple levels of attribute access are allowed
535            'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
536
537            # Fail silently when a variable's attribute isn't found
538            'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, ("","INVALID")),
539
540            # Raise TemplateSyntaxError when trying to access a variable beginning with an underscore
541            'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
542
543            # Raise TemplateSyntaxError when trying to access a variable containing an illegal character
544            'basic-syntax13': ("{{ va>r }}", {}, template.TemplateSyntaxError),
545            'basic-syntax14': ("{{ (var.r) }}", {}, template.TemplateSyntaxError),
546            'basic-syntax15': ("{{ sp%am }}", {}, template.TemplateSyntaxError),
547            'basic-syntax16': ("{{ eggs! }}", {}, template.TemplateSyntaxError),
548            'basic-syntax17': ("{{ moo? }}", {}, template.TemplateSyntaxError),
549
550            # Attribute syntax allows a template to call a dictionary key's value
551            'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
552
553            # Fail silently when a variable's dictionary key isn't found
554            'basic-syntax19': ("{{ foo.spam }}", {"foo" : {"bar" : "baz"}}, ("","INVALID")),
555
556            # Fail silently when accessing a non-simple method
557            'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("","INVALID")),
558
559            # Don't get confused when parsing something that is almost, but not
560            # quite, a template tag.
561            'basic-syntax21': ("a {{ moo %} b", {}, "a {{ moo %} b"),
562            'basic-syntax22': ("{{ moo #}", {}, "{{ moo #}"),
563
564            # Will try to treat "moo #} {{ cow" as the variable. Not ideal, but
565            # costly to work around, so this triggers an error.
566            'basic-syntax23': ("{{ moo #} {{ cow }}", {"cow": "cow"}, template.TemplateSyntaxError),
567
568            # Embedded newlines make it not-a-tag.
569            'basic-syntax24': ("{{ moo\n }}", {}, "{{ moo\n }}"),
570
571            # Literal strings are permitted inside variables, mostly for i18n
572            # purposes.
573            'basic-syntax25': ('{{ "fred" }}', {}, "fred"),
574            'basic-syntax26': (r'{{ "\"fred\"" }}', {}, "\"fred\""),
575            'basic-syntax27': (r'{{ _("\"fred\"") }}', {}, "\"fred\""),
576
577            # regression test for ticket #12554
578            # make sure a silent_variable_failure Exception is supressed
579            # on dictionary and attribute lookup
580            'basic-syntax28': ("{{ a.b }}", {'a': SilentGetItemClass()}, ('', 'INVALID')),
581            'basic-syntax29': ("{{ a.b }}", {'a': SilentAttrClass()}, ('', 'INVALID')),
582
583            # Something that starts like a number but has an extra lookup works as a lookup.
584            'basic-syntax30': ("{{ 1.2.3 }}", {"1": {"2": {"3": "d"}}}, "d"),
585            'basic-syntax31': ("{{ 1.2.3 }}", {"1": {"2": ("a", "b", "c", "d")}}, "d"),
586            'basic-syntax32': ("{{ 1.2.3 }}", {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, "d"),
587            'basic-syntax33': ("{{ 1.2.3 }}", {"1": ("xxxx", "yyyy", "abcd")}, "d"),
588            'basic-syntax34': ("{{ 1.2.3 }}", {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})}, "d"),
589
590            # Numbers are numbers even if their digits are in the context.
591            'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"),
592            'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"),
593
594            # Call methods in the top level of the context
595            'basic-syntax37': ('{{ callable }}', {"callable": lambda: "foo bar"}, "foo bar"),
596
597            # Call methods returned from dictionary lookups
598            'basic-syntax38': ('{{ var.callable }}', {"var": {"callable": lambda: "foo bar"}}, "foo bar"),
599
600            # List-index syntax allows a template to access a certain item of a subscriptable object.
601            'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
602
603            # Fail silently when the list index is out of range.
604            'list-index02': ("{{ var.5 }}", {"var": ["first item", "second item"]}, ("", "INVALID")),
605
606            # Fail silently when the variable is not a subscriptable object.
607            'list-index03': ("{{ var.1 }}", {"var": None}, ("", "INVALID")),
608
609            # Fail silently when variable is a dict without the specified key.
610            'list-index04': ("{{ var.1 }}", {"var": {}}, ("", "INVALID")),
611
612            # Dictionary lookup wins out when dict's key is a string.
613            'list-index05': ("{{ var.1 }}", {"var": {'1': "hello"}}, "hello"),
614
615            # But list-index lookup wins out when dict's key is an int, which
616            # behind the scenes is really a dictionary lookup (for a dict)
617            # after converting the key to an int.
618            'list-index06': ("{{ var.1 }}", {"var": {1: "hello"}}, "hello"),
619
620            # Dictionary lookup wins out when there is a string and int version of the key.
621            'list-index07': ("{{ var.1 }}", {"var": {'1': "hello", 1: "world"}}, "hello"),
622
623            # Basic filter usage
624            'filter-syntax01': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"),
625
626            # Chained filters
627            'filter-syntax02': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"),
628
629            # Raise TemplateSyntaxError for space between a variable and filter pipe
630            'filter-syntax03': ("{{ var |upper }}", {}, template.TemplateSyntaxError),
631
632            # Raise TemplateSyntaxError for space after a filter pipe
633            'filter-syntax04': ("{{ var| upper }}", {}, template.TemplateSyntaxError),
634
635            # Raise TemplateSyntaxError for a nonexistent filter
636            'filter-syntax05': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError),
637
638            # Raise TemplateSyntaxError when trying to access a filter containing an illegal character
639            'filter-syntax06': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError),
640
641            # Raise TemplateSyntaxError for invalid block tags
642            'filter-syntax07': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError),
643
644            # Raise TemplateSyntaxError for empty block tags
645            'filter-syntax08': ("{% %}", {}, template.TemplateSyntaxError),
646
647            # Chained filters, with an argument to the first one
648            'filter-syntax09': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
649
650            # Literal string as argument is always "safe" from auto-escaping..
651            'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}',
652                    {"var": None}, ' endquote" hah'),
653
654            # Variable as argument
655            'filter-syntax11': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'),
656
657            # Default argument testing
658            'filter-syntax12': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
659
660            # Fail silently for methods that raise an exception with a
661            # "silent_variable_failure" attribute
662            'filter-syntax13': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
663
664            # In methods that raise an exception without a
665            # "silent_variable_attribute" set to True, the exception propagates
666            'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
667
668            # Escaped backslash in argument
669            'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'),
670
671            # Escaped backslash using known escape char
672            'filter-syntax16': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'),
673
674            # Empty strings can be passed as arguments to filters
675            'filter-syntax17': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
676
677            # Make sure that any unicode strings are converted to bytestrings
678            # in the final output.
679            'filter-syntax18': (r'{{ var }}', {'var': UTF8Class()}, u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'),
680
681            # Numbers as filter arguments should work
682            'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."),
683
684            #filters should accept empty string constants
685            'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""),
686
687            # Fail silently for non-callable attribute and dict lookups which
688            # raise an exception with a "silent_variable_failure" attribute
689            'filter-syntax21': (r'1{{ var.silent_fail_key }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
690            'filter-syntax22': (r'1{{ var.silent_fail_attribute }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
691
692            # In attribute and dict lookups that raise an unexpected exception
693            # without a "silent_variable_attribute" set to True, the exception
694            # propagates
695            'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
696            'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException, template.TemplateSyntaxError)),
697
698            ### COMMENT SYNTAX ########################################################
699            'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
700            'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
701
702            # Comments can contain invalid stuff.
703            'comment-syntax03': ("foo{#  {% if %}  #}", {}, "foo"),
704            'comment-syntax04': ("foo{#  {% endblock %}  #}", {}, "foo"),
705            'comment-syntax05': ("foo{#  {% somerandomtag %}  #}", {}, "foo"),
706            'comment-syntax06': ("foo{# {% #}", {}, "foo"),
707            'comment-syntax07': ("foo{# %} #}", {}, "foo"),
708            'comment-syntax08': ("foo{# %} #}bar", {}, "foobar"),
709            'comment-syntax09': ("foo{# {{ #}", {}, "foo"),
710            'comment-syntax10': ("foo{# }} #}", {}, "foo"),
711            'comment-syntax11': ("foo{# { #}", {}, "foo"),
712            'comment-syntax12': ("foo{# } #}", {}, "foo"),
713
714            ### COMMENT TAG ###########################################################
715            'comment-tag01': ("{% comment %}this is hidden{% endcomment %}hello", {}, "hello"),
716            'comment-tag02': ("{% comment %}this is hidden{% endcomment %}hello{% comment %}foo{% endcomment %}", {}, "hello"),
717
718            # Comment tag can contain invalid stuff.
719            'comment-tag03': ("foo{% comment %} {% if %} {% endcomment %}", {}, "foo"),
720            'comment-tag04': ("foo{% comment %} {% endblock %} {% endcomment %}", {}, "foo"),
721            'comment-tag05': ("foo{% comment %} {% somerandomtag %} {% endcomment %}", {}, "foo"),
722
723            ### CYCLE TAG #############################################################
724            'cycle01': ('{% cycle a %}', {}, template.TemplateSyntaxError),
725            'cycle02': ('{% cycle a,b,c as abc %}{% cycle abc %}', {}, 'ab'),
726            'cycle03': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}', {}, 'abc'),
727            'cycle04': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}', {}, 'abca'),
728            'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
729            'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
730            'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
731            'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
732            'cycle09': ("{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
733            'cycle10': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}", {}, 'ab'),
734            'cycle11': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}", {}, 'abc'),
735            'cycle12': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, 'abca'),
736            'cycle13': ("{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
737            'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'),
738            'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'),
739            'cycle16': ("{% cycle one|lower two as foo %}{% cycle foo %}", {'one': 'A','two': '2'}, 'a2'),
740            'cycle17': ("{% cycle 'a' 'b' 'c' as abc silent %}{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, ""),
741            'cycle18': ("{% cycle 'a' 'b' 'c' as foo invalid_flag %}", {}, template.TemplateSyntaxError),
742            'cycle19': ("{% cycle 'a' 'b' as silent %}{% cycle silent %}", {}, "ab"),
743            'cycle20': ("{% cycle one two as foo %} &amp; {% cycle foo %}", {'one' : 'A & B', 'two' : 'C & D'}, "A & B &amp; C & D"),
744            'cycle21': ("{% filter force_escape %}{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}", {'one' : 'A & B', 'two' : 'C & D'}, "A &amp; B &amp; C &amp; D"),
745            'cycle22': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ x }}{% endfor %}", {'values': [1,2,3,4]}, "1234"),
746            'cycle23': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ abc }}{{ x }}{% endfor %}", {'values': [1,2,3,4]}, "a1b2c3a4"),
747            'included-cycle': ('{{ abc }}', {'abc': 'xxx'}, 'xxx'),
748            'cycle24': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", {'values': [1,2,3,4]}, "abca"),
749
750            ### EXCEPTIONS ############################################################
751
752            # Raise exception for invalid template name
753            'exception01': ("{% extends 'nonexistent' %}", {}, (template.TemplateDoesNotExist, template.TemplateDoesNotExist, template.TemplateSyntaxError)),
754
755            # Raise exception for invalid template name (in variable)
756            'exception02': ("{% extends nonexistent %}", {}, (template.TemplateSyntaxError, template.TemplateDoesNotExist)),
757
758            # Raise exception for extra {% extends %} tags
759            'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError),
760
761            # Raise exception for custom tags used in child with {% load %} tag in parent, not in child
762            'exception04': ("{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}", {}, template.TemplateSyntaxError),
763
764            ### FILTER TAG ############################################################
765            'filter01': ('{% filter upper %}{% endfilter %}', {}, ''),
766            'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'),
767            'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'),
768            'filter04': ('{% filter cut:remove %}djangospam{% endfilter %}', {'remove': 'spam'}, 'django'),
769
770            ### FIRSTOF TAG ###########################################################
771            'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''),
772            'firstof02': ('{% firstof a b c %}', {'a':1,'b':0,'c':0}, '1'),
773            'firstof03': ('{% firstof a b c %}', {'a':0,'b':2,'c':0}, '2'),
774            'firstof04': ('{% firstof a b c %}', {'a':0,'b':0,'c':3}, '3'),
775            'firstof05': ('{% firstof a b c %}', {'a':1,'b':2,'c':3}, '1'),
776            'firstof06': ('{% firstof a b c %}', {'b':0,'c':3}, '3'),
777            'firstof07': ('{% firstof a b "c" %}', {'a':0}, 'c'),
778            'firstof08': ('{% firstof a b "c and d" %}', {'a':0,'b':0}, 'c and d'),
779            'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError),
780
781            ### FOR TAG ###############################################################
782            'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"),
783            'for-tag02': ("{% for val in values reversed %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "321"),
784            'for-tag-vars01': ("{% for val in values %}{{ forloop.counter }}{% endfor %}", {"values": [6, 6, 6]}, "123"),
785            'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"),
786            'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"),
787            'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"),
788            'for-tag-vars05': ("{% for val in values %}{% if forloop.first %}f{% else %}x{% endif %}{% endfor %}", {"values": [6, 6, 6]}, "fxx"),
789            'for-tag-vars06': ("{% for val in values %}{% if forloop.last %}l{% else %}x{% endif %}{% endfor %}", {"values": [6, 6, 6]}, "xxl"),
790            'for-tag-unpack01': ("{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
791            'for-tag-unpack03': ("{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
792            'for-tag-unpack04': ("{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
793            'for-tag-unpack05': ("{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
794            'for-tag-unpack06': ("{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError),
795            'for-tag-unpack07': ("{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError),
796            'for-tag-unpack08': ("{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError),
797            # Ensure that a single loopvar doesn't truncate the list in val.
798            'for-tag-unpack09': ("{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
799            # Otherwise, silently truncate if the length of loopvars differs to the length of each set of items.
800            'for-tag-unpack10': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'orange'))}, "one:1/two:2/"),
801            'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")),
802            'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")),
803            'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")),
804            'for-tag-unpack14': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (1, 2)}, (":/:/", "INVALID:INVALID/INVALID:INVALID/")),
805            'for-tag-empty01': ("{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}", {"values": [1, 2, 3]}, "123"),
806            'for-tag-empty02': ("{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}", {"values": []}, "values array empty"),
807            'for-tag-empty03': ("{% for val in values %}{{ val }}{% empty %}values array not found{% endfor %}", {}, "values array not found"),
808
809            ### IF TAG ################################################################
810            'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
811            'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
812            'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
813
814            # Filters
815            'if-tag-filter01': ("{% if foo|length == 5 %}yes{% else %}no{% endif %}", {'foo': 'abcde'}, "yes"),
816            'if-tag-filter02': ("{% if foo|upper == 'ABC' %}yes{% else %}no{% endif %}", {}, "no"),
817
818            # Equality
819            'if-tag-eq01': ("{% if foo == bar %}yes{% else %}no{% endif %}", {}, "yes"),
820            'if-tag-eq02': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1}, "no"),
821            'if-tag-eq03': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 1}, "yes"),
822            'if-tag-eq04': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 2}, "no"),
823            'if-tag-eq05': ("{% if foo == '' %}yes{% else %}no{% endif %}", {}, "no"),
824
825            # Comparison
826            'if-tag-gt-01': ("{% if 2 > 1 %}yes{% else %}no{% endif %}", {}, "yes"),
827            'if-tag-gt-02': ("{% if 1 > 1 %}yes{% else %}no{% endif %}", {}, "no"),
828            'if-tag-gte-01': ("{% if 1 >= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
829            'if-tag-gte-02': ("{% if 1 >= 2 %}yes{% else %}no{% endif %}", {}, "no"),
830            'if-tag-lt-01': ("{% if 1 < 2 %}yes{% else %}no{% endif %}", {}, "yes"),
831            'if-tag-lt-02': ("{% if 1 < 1 %}yes{% else %}no{% endif %}", {}, "no"),
832            'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
833            'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"),
834
835            # Contains
836            'if-tag-in-01': ("{% if 1 in x %}yes{% else %}no{% endif %}", {'x':[1]}, "yes"),
837            'if-tag-in-02': ("{% if 2 in x %}yes{% else %}no{% endif %}", {'x':[1]}, "no"),
838            'if-tag-not-in-01': ("{% if 1 not in x %}yes{% else %}no{% endif %}", {'x':[1]}, "no"),
839            'if-tag-not-in-02': ("{% if 2 not in x %}yes{% else %}no{% endif %}", {'x':[1]}, "yes"),
840
841            # AND
842            'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
843            'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
844            'if-tag-and03': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
845            'if-tag-and04': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
846            'if-tag-and05': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
847            'if-tag-and06': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
848            'if-tag-and07': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
849            'if-tag-and08': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': True}, 'no'),
850
851            # OR
852            'if-tag-or01': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
853            'if-tag-or02': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
854            'if-tag-or03': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
855            'if-tag-or04': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
856            'if-tag-or05': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
857            'if-tag-or06': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
858            'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
859            'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
860
861            # multiple ORs
862            'if-tag-or09': ("{% if foo or bar or baz %}yes{% else %}no{% endif %}", {'baz': True}, 'yes'),
863
864            # NOT
865            'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'),
866            'if-tag-not02': ("{% if not not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'no'),
867            # not03 to not05 removed, now TemplateSyntaxErrors
868
869            'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'),
870            'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
871            'if-tag-not08': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
872            'if-tag-not09': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
873            'if-tag-not10': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
874
875            'if-tag-not11': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {}, 'no'),
876            'if-tag-not12': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
877            'if-tag-not13': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
878            'if-tag-not14': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
879            'if-tag-not15': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
880
881            'if-tag-not16': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
882            'if-tag-not17': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
883            'if-tag-not18': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
884            'if-tag-not19': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
885            'if-tag-not20': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
886
887            'if-tag-not21': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {}, 'yes'),
888            'if-tag-not22': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
889            'if-tag-not23': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
890            'if-tag-not24': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
891            'if-tag-not25': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
892
893            'if-tag-not26': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
894            'if-tag-not27': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
895            'if-tag-not28': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
896            'if-tag-not29': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
897            'if-tag-not30': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo'

Large files files are truncated, but you can click here to view the full file