~ubuntu-branches/ubuntu/saucy/python-django/saucy-updates

« back to all changes in this revision

Viewing changes to tests/regressiontests/templates/tests.py

  • Committer: Bazaar Package Importer
  • Author(s): Chris Lamb
  • Date: 2009-07-29 11:26:28 UTC
  • mfrom: (1.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 22.
  • Revision ID: james.westby@ubuntu.com-20090729112628-9qrzwnl9x32jxhbg
Tags: upstream-1.1
Import upstream version 1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
from django.conf import settings
 
3
 
 
4
if __name__ == '__main__':
 
5
    # When running this file in isolation, we need to set up the configuration
 
6
    # before importing 'template'.
 
7
    settings.configure()
 
8
 
 
9
from datetime import datetime, timedelta
 
10
import os
 
11
import sys
 
12
import traceback
 
13
import unittest
 
14
 
 
15
from django import template
 
16
from django.core import urlresolvers
 
17
from django.template import loader
 
18
from django.template.loaders import app_directories, filesystem
 
19
from django.utils.translation import activate, deactivate, ugettext as _
 
20
from django.utils.safestring import mark_safe
 
21
from django.utils.tzinfo import LocalTimezone
 
22
 
 
23
from context import context_tests
 
24
from custom import custom_filters
 
25
from parser import filter_parsing, variable_parsing
 
26
from unicode import unicode_tests
 
27
 
 
28
try:
 
29
    from loaders import *
 
30
except ImportError:
 
31
    pass # If setuptools isn't installed, that's fine. Just move on.
 
32
 
 
33
import filters
 
34
 
 
35
# Some other tests we would like to run
 
36
__test__ = {
 
37
    'unicode': unicode_tests,
 
38
    'context': context_tests,
 
39
    'filter_parsing': filter_parsing,
 
40
    'custom_filters': custom_filters,
 
41
}
 
42
 
 
43
#################################
 
44
# Custom template tag for tests #
 
45
#################################
 
46
 
 
47
register = template.Library()
 
48
 
 
49
class EchoNode(template.Node):
 
50
    def __init__(self, contents):
 
51
        self.contents = contents
 
52
 
 
53
    def render(self, context):
 
54
        return " ".join(self.contents)
 
55
 
 
56
def do_echo(parser, token):
 
57
    return EchoNode(token.contents.split()[1:])
 
58
 
 
59
register.tag("echo", do_echo)
 
60
 
 
61
template.libraries['django.templatetags.testtags'] = register
 
62
 
 
63
#####################################
 
64
# Helper objects for template tests #
 
65
#####################################
 
66
 
 
67
class SomeException(Exception):
 
68
    silent_variable_failure = True
 
69
 
 
70
class SomeOtherException(Exception):
 
71
    pass
 
72
 
 
73
class ContextStackException(Exception):
 
74
    pass
 
75
 
 
76
class SomeClass:
 
77
    def __init__(self):
 
78
        self.otherclass = OtherClass()
 
79
 
 
80
    def method(self):
 
81
        return "SomeClass.method"
 
82
 
 
83
    def method2(self, o):
 
84
        return o
 
85
 
 
86
    def method3(self):
 
87
        raise SomeException
 
88
 
 
89
    def method4(self):
 
90
        raise SomeOtherException
 
91
 
 
92
class OtherClass:
 
93
    def method(self):
 
94
        return "OtherClass.method"
 
95
 
 
96
class UTF8Class:
 
97
    "Class whose __str__ returns non-ASCII data"
 
98
    def __str__(self):
 
99
        return u'ŠĐĆŽćžšđ'.encode('utf-8')
 
100
 
 
101
class Templates(unittest.TestCase):
 
102
    def test_loaders_security(self):
 
103
        def test_template_sources(path, template_dirs, expected_sources):
 
104
            if isinstance(expected_sources, list):
 
105
                # Fix expected sources so they are normcased and abspathed
 
106
                expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
 
107
            # Test the two loaders (app_directores and filesystem).
 
108
            func1 = lambda p, t: list(app_directories.get_template_sources(p, t))
 
109
            func2 = lambda p, t: list(filesystem.get_template_sources(p, t))
 
110
            for func in (func1, func2):
 
111
                if isinstance(expected_sources, list):
 
112
                    self.assertEqual(func(path, template_dirs), expected_sources)
 
113
                else:
 
114
                    self.assertRaises(expected_sources, func, path, template_dirs)
 
115
 
 
116
        template_dirs = ['/dir1', '/dir2']
 
117
        test_template_sources('index.html', template_dirs,
 
118
                              ['/dir1/index.html', '/dir2/index.html'])
 
119
        test_template_sources('/etc/passwd', template_dirs, [])
 
120
        test_template_sources('etc/passwd', template_dirs,
 
121
                              ['/dir1/etc/passwd', '/dir2/etc/passwd'])
 
122
        test_template_sources('../etc/passwd', template_dirs, [])
 
123
        test_template_sources('../../../etc/passwd', template_dirs, [])
 
124
        test_template_sources('/dir1/index.html', template_dirs,
 
125
                              ['/dir1/index.html'])
 
126
        test_template_sources('../dir2/index.html', template_dirs,
 
127
                              ['/dir2/index.html'])
 
128
        test_template_sources('/dir1blah', template_dirs, [])
 
129
        test_template_sources('../dir1blah', template_dirs, [])
 
130
 
 
131
        # UTF-8 bytestrings are permitted.
 
132
        test_template_sources('\xc3\x85ngstr\xc3\xb6m', template_dirs,
 
133
                              [u'/dir1/Ångström', u'/dir2/Ångström'])
 
134
        # Unicode strings are permitted.
 
135
        test_template_sources(u'Ångström', template_dirs,
 
136
                              [u'/dir1/Ångström', u'/dir2/Ångström'])
 
137
        test_template_sources(u'Ångström', ['/Straße'], [u'/Straße/Ångström'])
 
138
        test_template_sources('\xc3\x85ngstr\xc3\xb6m', ['/Straße'],
 
139
                              [u'/Straße/Ångström'])
 
140
        # Invalid UTF-8 encoding in bytestrings is not. Should raise a
 
141
        # semi-useful error message.
 
142
        test_template_sources('\xc3\xc3', template_dirs, UnicodeDecodeError)
 
143
 
 
144
        # Case insensitive tests (for win32). Not run unless we're on
 
145
        # a case insensitive operating system.
 
146
        if os.path.normcase('/TEST') == os.path.normpath('/test'):
 
147
            template_dirs = ['/dir1', '/DIR2']
 
148
            test_template_sources('index.html', template_dirs,
 
149
                                  ['/dir1/index.html', '/dir2/index.html'])
 
150
            test_template_sources('/DIR1/index.HTML', template_dirs,
 
151
                                  ['/dir1/index.html'])
 
152
 
 
153
    def test_token_smart_split(self):
 
154
        # Regression test for #7027
 
155
        token = template.Token(template.TOKEN_BLOCK, 'sometag _("Page not found") value|yesno:_("yes,no")')
 
156
        split = token.split_contents()
 
157
        self.assertEqual(split, ["sometag", '_("Page not found")', 'value|yesno:_("yes,no")'])
 
158
 
 
159
    def test_url_reverse_no_settings_module(self):
 
160
        # Regression test for #9005
 
161
        from django.template import Template, Context, TemplateSyntaxError
 
162
 
 
163
        old_settings_module = settings.SETTINGS_MODULE
 
164
        old_template_debug = settings.TEMPLATE_DEBUG
 
165
 
 
166
        settings.SETTINGS_MODULE = None
 
167
        settings.TEMPLATE_DEBUG = True
 
168
 
 
169
        t = Template('{% url will_not_match %}')
 
170
        c = Context()
 
171
        try:
 
172
            rendered = t.render(c)
 
173
        except TemplateSyntaxError, e:
 
174
            # Assert that we are getting the template syntax error and not the
 
175
            # string encoding error.
 
176
            self.assertEquals(e.args[0], "Caught an exception while rendering: Reverse for 'will_not_match' with arguments '()' and keyword arguments '{}' not found.")
 
177
 
 
178
        settings.SETTINGS_MODULE = old_settings_module
 
179
        settings.TEMPLATE_DEBUG = old_template_debug
 
180
 
 
181
    def test_templates(self):
 
182
        template_tests = self.get_template_tests()
 
183
        filter_tests = filters.get_filter_tests()
 
184
 
 
185
        # Quickly check that we aren't accidentally using a name in both
 
186
        # template and filter tests.
 
187
        overlapping_names = [name for name in filter_tests if name in template_tests]
 
188
        assert not overlapping_names, 'Duplicate test name(s): %s' % ', '.join(overlapping_names)
 
189
 
 
190
        template_tests.update(filter_tests)
 
191
 
 
192
        # Register our custom template loader.
 
193
        def test_template_loader(template_name, template_dirs=None):
 
194
            "A custom template loader that loads the unit-test templates."
 
195
            try:
 
196
                return (template_tests[template_name][0] , "test:%s" % template_name)
 
197
            except KeyError:
 
198
                raise template.TemplateDoesNotExist, template_name
 
199
 
 
200
        old_template_loaders = loader.template_source_loaders
 
201
        loader.template_source_loaders = [test_template_loader]
 
202
 
 
203
        failures = []
 
204
        tests = template_tests.items()
 
205
        tests.sort()
 
206
 
 
207
        # Turn TEMPLATE_DEBUG off, because tests assume that.
 
208
        old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
 
209
 
 
210
        # Set TEMPLATE_STRING_IF_INVALID to a known string.
 
211
        old_invalid = settings.TEMPLATE_STRING_IF_INVALID
 
212
        expected_invalid_str = 'INVALID'
 
213
 
 
214
        for name, vals in tests:
 
215
            if isinstance(vals[2], tuple):
 
216
                normal_string_result = vals[2][0]
 
217
                invalid_string_result = vals[2][1]
 
218
                if '%s' in invalid_string_result:
 
219
                    expected_invalid_str = 'INVALID %s'
 
220
                    invalid_string_result = invalid_string_result % vals[2][2]
 
221
                    template.invalid_var_format_string = True
 
222
            else:
 
223
                normal_string_result = vals[2]
 
224
                invalid_string_result = vals[2]
 
225
 
 
226
            if 'LANGUAGE_CODE' in vals[1]:
 
227
                activate(vals[1]['LANGUAGE_CODE'])
 
228
            else:
 
229
                activate('en-us')
 
230
 
 
231
            for invalid_str, result in [('', normal_string_result),
 
232
                                        (expected_invalid_str, invalid_string_result)]:
 
233
                settings.TEMPLATE_STRING_IF_INVALID = invalid_str
 
234
                try:
 
235
                    test_template = loader.get_template(name)
 
236
                    output = self.render(test_template, vals)
 
237
                except ContextStackException:
 
238
                    failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (invalid_str, name))
 
239
                    continue
 
240
                except Exception:
 
241
                    exc_type, exc_value, exc_tb = sys.exc_info()
 
242
                    if exc_type != result:
 
243
                        tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
 
244
                        failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s\n%s" % (invalid_str, name, exc_type, exc_value, tb))
 
245
                    continue
 
246
                if output != result:
 
247
                    failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
 
248
 
 
249
            if 'LANGUAGE_CODE' in vals[1]:
 
250
                deactivate()
 
251
 
 
252
            if template.invalid_var_format_string:
 
253
                expected_invalid_str = 'INVALID'
 
254
                template.invalid_var_format_string = False
 
255
 
 
256
        loader.template_source_loaders = old_template_loaders
 
257
        deactivate()
 
258
        settings.TEMPLATE_DEBUG = old_td
 
259
        settings.TEMPLATE_STRING_IF_INVALID = old_invalid
 
260
 
 
261
        self.assertEqual(failures, [], "Tests failed:\n%s\n%s" %
 
262
            ('-'*70, ("\n%s\n" % ('-'*70)).join(failures)))
 
263
 
 
264
    def render(self, test_template, vals):
 
265
        context = template.Context(vals[1])
 
266
        before_stack_size = len(context.dicts)
 
267
        output = test_template.render(context)
 
268
        if len(context.dicts) != before_stack_size:
 
269
            raise ContextStackException
 
270
        return output
 
271
 
 
272
    def get_template_tests(self):
 
273
        # SYNTAX --
 
274
        # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class)
 
275
        return {
 
276
            ### BASIC SYNTAX ################################################
 
277
 
 
278
            # Plain text should go through the template parser untouched
 
279
            'basic-syntax01': ("something cool", {}, "something cool"),
 
280
 
 
281
            # Variables should be replaced with their value in the current
 
282
            # context
 
283
            'basic-syntax02': ("{{ headline }}", {'headline':'Success'}, "Success"),
 
284
 
 
285
            # More than one replacement variable is allowed in a template
 
286
            'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
 
287
 
 
288
            # Fail silently when a variable is not found in the current context
 
289
            'basic-syntax04': ("as{{ missing }}df", {}, ("asdf","asINVALIDdf")),
 
290
 
 
291
            # A variable may not contain more than one word
 
292
            'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
 
293
 
 
294
            # Raise TemplateSyntaxError for empty variable tags
 
295
            'basic-syntax07': ("{{ }}",        {}, template.TemplateSyntaxError),
 
296
            'basic-syntax08': ("{{        }}", {}, template.TemplateSyntaxError),
 
297
 
 
298
            # Attribute syntax allows a template to call an object's attribute
 
299
            'basic-syntax09': ("{{ var.method }}", {"var": SomeClass()}, "SomeClass.method"),
 
300
 
 
301
            # Multiple levels of attribute access are allowed
 
302
            'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
 
303
 
 
304
            # Fail silently when a variable's attribute isn't found
 
305
            'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, ("","INVALID")),
 
306
 
 
307
            # Raise TemplateSyntaxError when trying to access a variable beginning with an underscore
 
308
            'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
 
309
 
 
310
            # Raise TemplateSyntaxError when trying to access a variable containing an illegal character
 
311
            'basic-syntax13': ("{{ va>r }}", {}, template.TemplateSyntaxError),
 
312
            'basic-syntax14': ("{{ (var.r) }}", {}, template.TemplateSyntaxError),
 
313
            'basic-syntax15': ("{{ sp%am }}", {}, template.TemplateSyntaxError),
 
314
            'basic-syntax16': ("{{ eggs! }}", {}, template.TemplateSyntaxError),
 
315
            'basic-syntax17': ("{{ moo? }}", {}, template.TemplateSyntaxError),
 
316
 
 
317
            # Attribute syntax allows a template to call a dictionary key's value
 
318
            'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
 
319
 
 
320
            # Fail silently when a variable's dictionary key isn't found
 
321
            'basic-syntax19': ("{{ foo.spam }}", {"foo" : {"bar" : "baz"}}, ("","INVALID")),
 
322
 
 
323
            # Fail silently when accessing a non-simple method
 
324
            'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("","INVALID")),
 
325
 
 
326
            # Don't get confused when parsing something that is almost, but not
 
327
            # quite, a template tag.
 
328
            'basic-syntax21': ("a {{ moo %} b", {}, "a {{ moo %} b"),
 
329
            'basic-syntax22': ("{{ moo #}", {}, "{{ moo #}"),
 
330
 
 
331
            # Will try to treat "moo #} {{ cow" as the variable. Not ideal, but
 
332
            # costly to work around, so this triggers an error.
 
333
            'basic-syntax23': ("{{ moo #} {{ cow }}", {"cow": "cow"}, template.TemplateSyntaxError),
 
334
 
 
335
            # Embedded newlines make it not-a-tag.
 
336
            'basic-syntax24': ("{{ moo\n }}", {}, "{{ moo\n }}"),
 
337
 
 
338
            # Literal strings are permitted inside variables, mostly for i18n
 
339
            # purposes.
 
340
            'basic-syntax25': ('{{ "fred" }}', {}, "fred"),
 
341
            'basic-syntax26': (r'{{ "\"fred\"" }}', {}, "\"fred\""),
 
342
            'basic-syntax27': (r'{{ _("\"fred\"") }}', {}, "\"fred\""),
 
343
 
 
344
            # List-index syntax allows a template to access a certain item of a subscriptable object.
 
345
            'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
 
346
 
 
347
            # Fail silently when the list index is out of range.
 
348
            'list-index02': ("{{ var.5 }}", {"var": ["first item", "second item"]}, ("", "INVALID")),
 
349
 
 
350
            # Fail silently when the variable is not a subscriptable object.
 
351
            'list-index03': ("{{ var.1 }}", {"var": None}, ("", "INVALID")),
 
352
 
 
353
            # Fail silently when variable is a dict without the specified key.
 
354
            'list-index04': ("{{ var.1 }}", {"var": {}}, ("", "INVALID")),
 
355
 
 
356
            # Dictionary lookup wins out when dict's key is a string.
 
357
            'list-index05': ("{{ var.1 }}", {"var": {'1': "hello"}}, "hello"),
 
358
 
 
359
            # But list-index lookup wins out when dict's key is an int, which
 
360
            # behind the scenes is really a dictionary lookup (for a dict)
 
361
            # after converting the key to an int.
 
362
            'list-index06': ("{{ var.1 }}", {"var": {1: "hello"}}, "hello"),
 
363
 
 
364
            # Dictionary lookup wins out when there is a string and int version of the key.
 
365
            'list-index07': ("{{ var.1 }}", {"var": {'1': "hello", 1: "world"}}, "hello"),
 
366
 
 
367
            # Basic filter usage
 
368
            'filter-syntax01': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"),
 
369
 
 
370
            # Chained filters
 
371
            'filter-syntax02': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"),
 
372
 
 
373
            # Raise TemplateSyntaxError for space between a variable and filter pipe
 
374
            'filter-syntax03': ("{{ var |upper }}", {}, template.TemplateSyntaxError),
 
375
 
 
376
            # Raise TemplateSyntaxError for space after a filter pipe
 
377
            'filter-syntax04': ("{{ var| upper }}", {}, template.TemplateSyntaxError),
 
378
 
 
379
            # Raise TemplateSyntaxError for a nonexistent filter
 
380
            'filter-syntax05': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError),
 
381
 
 
382
            # Raise TemplateSyntaxError when trying to access a filter containing an illegal character
 
383
            'filter-syntax06': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError),
 
384
 
 
385
            # Raise TemplateSyntaxError for invalid block tags
 
386
            'filter-syntax07': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError),
 
387
 
 
388
            # Raise TemplateSyntaxError for empty block tags
 
389
            'filter-syntax08': ("{% %}", {}, template.TemplateSyntaxError),
 
390
 
 
391
            # Chained filters, with an argument to the first one
 
392
            'filter-syntax09': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
 
393
 
 
394
            # Literal string as argument is always "safe" from auto-escaping..
 
395
            'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}',
 
396
                    {"var": None}, ' endquote" hah'),
 
397
 
 
398
            # Variable as argument
 
399
            'filter-syntax11': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'),
 
400
 
 
401
            # Default argument testing
 
402
            'filter-syntax12': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
 
403
 
 
404
            # Fail silently for methods that raise an exception with a
 
405
            # "silent_variable_failure" attribute
 
406
            'filter-syntax13': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
 
407
 
 
408
            # In methods that raise an exception without a
 
409
            # "silent_variable_attribute" set to True, the exception propagates
 
410
            'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException),
 
411
 
 
412
            # Escaped backslash in argument
 
413
            'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'),
 
414
 
 
415
            # Escaped backslash using known escape char
 
416
            'filter-syntax16': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'),
 
417
 
 
418
            # Empty strings can be passed as arguments to filters
 
419
            'filter-syntax17': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
 
420
 
 
421
            # Make sure that any unicode strings are converted to bytestrings
 
422
            # in the final output.
 
423
            'filter-syntax18': (r'{{ var }}', {'var': UTF8Class()}, u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'),
 
424
 
 
425
            # Numbers as filter arguments should work
 
426
            'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."),
 
427
 
 
428
            #filters should accept empty string constants
 
429
            'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""),
 
430
 
 
431
            ### COMMENT SYNTAX ########################################################
 
432
            'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
 
433
            'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
 
434
 
 
435
            # Comments can contain invalid stuff.
 
436
            'comment-syntax03': ("foo{#  {% if %}  #}", {}, "foo"),
 
437
            'comment-syntax04': ("foo{#  {% endblock %}  #}", {}, "foo"),
 
438
            'comment-syntax05': ("foo{#  {% somerandomtag %}  #}", {}, "foo"),
 
439
            'comment-syntax06': ("foo{# {% #}", {}, "foo"),
 
440
            'comment-syntax07': ("foo{# %} #}", {}, "foo"),
 
441
            'comment-syntax08': ("foo{# %} #}bar", {}, "foobar"),
 
442
            'comment-syntax09': ("foo{# {{ #}", {}, "foo"),
 
443
            'comment-syntax10': ("foo{# }} #}", {}, "foo"),
 
444
            'comment-syntax11': ("foo{# { #}", {}, "foo"),
 
445
            'comment-syntax12': ("foo{# } #}", {}, "foo"),
 
446
 
 
447
            ### COMMENT TAG ###########################################################
 
448
            'comment-tag01': ("{% comment %}this is hidden{% endcomment %}hello", {}, "hello"),
 
449
            'comment-tag02': ("{% comment %}this is hidden{% endcomment %}hello{% comment %}foo{% endcomment %}", {}, "hello"),
 
450
 
 
451
            # Comment tag can contain invalid stuff.
 
452
            'comment-tag03': ("foo{% comment %} {% if %} {% endcomment %}", {}, "foo"),
 
453
            'comment-tag04': ("foo{% comment %} {% endblock %} {% endcomment %}", {}, "foo"),
 
454
            'comment-tag05': ("foo{% comment %} {% somerandomtag %} {% endcomment %}", {}, "foo"),
 
455
 
 
456
            ### CYCLE TAG #############################################################
 
457
            'cycle01': ('{% cycle a %}', {}, template.TemplateSyntaxError),
 
458
            'cycle02': ('{% cycle a,b,c as abc %}{% cycle abc %}', {}, 'ab'),
 
459
            'cycle03': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}', {}, 'abc'),
 
460
            'cycle04': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}', {}, 'abca'),
 
461
            'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
 
462
            'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
 
463
            'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
 
464
            'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
 
465
            'cycle09': ("{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
 
466
            'cycle10': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}", {}, 'ab'),
 
467
            'cycle11': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}", {}, 'abc'),
 
468
            'cycle12': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, 'abca'),
 
469
            'cycle13': ("{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
 
470
            'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'),
 
471
            'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'),
 
472
            'cycle16': ("{% cycle one|lower two as foo %}{% cycle foo %}", {'one': 'A','two': '2'}, 'a2'),
 
473
 
 
474
            ### EXCEPTIONS ############################################################
 
475
 
 
476
            # Raise exception for invalid template name
 
477
            'exception01': ("{% extends 'nonexistent' %}", {}, template.TemplateSyntaxError),
 
478
 
 
479
            # Raise exception for invalid template name (in variable)
 
480
            'exception02': ("{% extends nonexistent %}", {}, template.TemplateSyntaxError),
 
481
 
 
482
            # Raise exception for extra {% extends %} tags
 
483
            'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError),
 
484
 
 
485
            # Raise exception for custom tags used in child with {% load %} tag in parent, not in child
 
486
            'exception04': ("{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}", {}, template.TemplateSyntaxError),
 
487
 
 
488
            ### FILTER TAG ############################################################
 
489
            'filter01': ('{% filter upper %}{% endfilter %}', {}, ''),
 
490
            'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'),
 
491
            'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'),
 
492
            'filter04': ('{% filter cut:remove %}djangospam{% endfilter %}', {'remove': 'spam'}, 'django'),
 
493
 
 
494
            ### FIRSTOF TAG ###########################################################
 
495
            'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''),
 
496
            'firstof02': ('{% firstof a b c %}', {'a':1,'b':0,'c':0}, '1'),
 
497
            'firstof03': ('{% firstof a b c %}', {'a':0,'b':2,'c':0}, '2'),
 
498
            'firstof04': ('{% firstof a b c %}', {'a':0,'b':0,'c':3}, '3'),
 
499
            'firstof05': ('{% firstof a b c %}', {'a':1,'b':2,'c':3}, '1'),
 
500
            'firstof06': ('{% firstof a b c %}', {'b':0,'c':3}, '3'),
 
501
            'firstof07': ('{% firstof a b "c" %}', {'a':0}, 'c'),
 
502
            'firstof08': ('{% firstof a b "c and d" %}', {'a':0,'b':0}, 'c and d'),
 
503
            'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError),
 
504
 
 
505
            ### FOR TAG ###############################################################
 
506
            'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"),
 
507
            'for-tag02': ("{% for val in values reversed %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "321"),
 
508
            'for-tag-vars01': ("{% for val in values %}{{ forloop.counter }}{% endfor %}", {"values": [6, 6, 6]}, "123"),
 
509
            'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"),
 
510
            'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"),
 
511
            'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"),
 
512
            'for-tag-vars05': ("{% for val in values %}{% if forloop.first %}f{% else %}x{% endif %}{% endfor %}", {"values": [6, 6, 6]}, "fxx"),
 
513
            'for-tag-vars06': ("{% for val in values %}{% if forloop.last %}l{% else %}x{% endif %}{% endfor %}", {"values": [6, 6, 6]}, "xxl"),
 
514
            'for-tag-unpack01': ("{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
 
515
            'for-tag-unpack03': ("{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
 
516
            'for-tag-unpack04': ("{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
 
517
            'for-tag-unpack05': ("{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
 
518
            'for-tag-unpack06': ("{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError),
 
519
            'for-tag-unpack07': ("{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError),
 
520
            'for-tag-unpack08': ("{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError),
 
521
            # Ensure that a single loopvar doesn't truncate the list in val.
 
522
            'for-tag-unpack09': ("{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"),
 
523
            # Otherwise, silently truncate if the length of loopvars differs to the length of each set of items.
 
524
            'for-tag-unpack10': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'orange'))}, "one:1/two:2/"),
 
525
            '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/")),
 
526
            '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/")),
 
527
            '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/")),
 
528
            'for-tag-empty01': ("{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}", {"values": [1, 2, 3]}, "123"),
 
529
            'for-tag-empty02': ("{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}", {"values": []}, "values array empty"),
 
530
            'for-tag-empty03': ("{% for val in values %}{{ val }}{% empty %}values array not found{% endfor %}", {}, "values array not found"),
 
531
 
 
532
            ### IF TAG ################################################################
 
533
            'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
 
534
            'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
 
535
            'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
 
536
 
 
537
            # AND
 
538
            'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
 
539
            'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
 
540
            'if-tag-and03': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
 
541
            'if-tag-and04': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
 
542
            'if-tag-and05': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
 
543
            'if-tag-and06': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
 
544
            'if-tag-and07': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
 
545
            'if-tag-and08': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': True}, 'no'),
 
546
 
 
547
            # OR
 
548
            'if-tag-or01': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
 
549
            'if-tag-or02': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
 
550
            'if-tag-or03': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
 
551
            'if-tag-or04': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
 
552
            'if-tag-or05': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
 
553
            'if-tag-or06': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
 
554
            'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
 
555
            'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
 
556
 
 
557
            # TODO: multiple ORs
 
558
 
 
559
            # NOT
 
560
            'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'),
 
561
            'if-tag-not02': ("{% if not %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
 
562
            'if-tag-not03': ("{% if not %}yes{% else %}no{% endif %}", {'not': True}, 'yes'),
 
563
            'if-tag-not04': ("{% if not not %}no{% else %}yes{% endif %}", {'not': True}, 'yes'),
 
564
            'if-tag-not05': ("{% if not not %}no{% else %}yes{% endif %}", {}, 'no'),
 
565
 
 
566
            'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'),
 
567
            'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
 
568
            'if-tag-not08': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
 
569
            'if-tag-not09': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
 
570
            'if-tag-not10': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
 
571
 
 
572
            'if-tag-not11': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {}, 'no'),
 
573
            'if-tag-not12': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
 
574
            'if-tag-not13': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
 
575
            'if-tag-not14': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
 
576
            'if-tag-not15': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
 
577
 
 
578
            'if-tag-not16': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
 
579
            'if-tag-not17': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
 
580
            'if-tag-not18': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
 
581
            'if-tag-not19': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
 
582
            'if-tag-not20': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
 
583
 
 
584
            'if-tag-not21': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {}, 'yes'),
 
585
            'if-tag-not22': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
 
586
            'if-tag-not23': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
 
587
            'if-tag-not24': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
 
588
            'if-tag-not25': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
 
589
 
 
590
            'if-tag-not26': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
 
591
            'if-tag-not27': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
 
592
            'if-tag-not28': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
 
593
            'if-tag-not29': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
 
594
            'if-tag-not30': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
 
595
 
 
596
            'if-tag-not31': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
 
597
            'if-tag-not32': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
 
598
            'if-tag-not33': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
 
599
            'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
 
600
            'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
 
601
 
 
602
            # AND and OR raises a TemplateSyntaxError
 
603
            'if-tag-error01': ("{% if foo or bar and baz %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, template.TemplateSyntaxError),
 
604
            'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
 
605
            'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
 
606
            'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
 
607
            'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
 
608
 
 
609
            ### IFCHANGED TAG #########################################################
 
610
            'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1,2,3)}, '123'),
 
611
            'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1,1,3)}, '13'),
 
612
            'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1,1,1)}, '1'),
 
613
            'ifchanged04': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 2, 3), 'numx': (2, 2, 2)}, '122232'),
 
614
            'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'),
 
615
            'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'),
 
616
            'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', {'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'),
 
617
            'ifchanged08': ('{% for data in datalist %}{% for c,d in data %}{% if c %}{% ifchanged %}{{ d }}{% endifchanged %}{% endif %}{% endfor %}{% endfor %}', {'datalist': [[(1, 'a'), (1, 'a'), (0, 'b'), (1, 'c')], [(0, 'a'), (1, 'c'), (1, 'd'), (1, 'd'), (0, 'e')]]}, 'accd'),
 
618
 
 
619
            # Test one parameter given to ifchanged.
 
620
            'ifchanged-param01': ('{% for n in num %}{% ifchanged n %}..{% endifchanged %}{{ n }}{% endfor %}', { 'num': (1,2,3) }, '..1..2..3'),
 
621
            'ifchanged-param02': ('{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}', { 'num': (1,2,3), 'numx': (5,6,7) }, '..567..567..567'),
 
622
 
 
623
            # Test multiple parameters to ifchanged.
 
624
            'ifchanged-param03': ('{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', { 'num': (1,1,2), 'numx': (5,6,6) }, '156156256'),
 
625
 
 
626
            # Test a date+hour like construct, where the hour of the last day
 
627
            # is the same but the date had changed, so print the hour anyway.
 
628
            'ifchanged-param04': ('{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
 
629
 
 
630
            # Logically the same as above, just written with explicit
 
631
            # ifchanged for the day.
 
632
            'ifchanged-param05': ('{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days':[{'day':1, 'hours':[1,2,3]},{'day':2, 'hours':[3]},] }, '112323'),
 
633
 
 
634
            # Test the else clause of ifchanged.
 
635
            'ifchanged-else01': ('{% for id in ids %}{{ id }}{% ifchanged id %}-first{% else %}-other{% endifchanged %},{% endfor %}', {'ids': [1,1,2,2,2,3]}, '1-first,1-other,2-first,2-other,2-other,3-first,'),
 
636
 
 
637
            'ifchanged-else02': ('{% for id in ids %}{{ id }}-{% ifchanged id %}{% cycle red,blue %}{% else %}grey{% endifchanged %},{% endfor %}', {'ids': [1,1,2,2,2,3]}, '1-red,1-grey,2-blue,2-grey,2-grey,3-red,'),
 
638
            'ifchanged-else03': ('{% for id in ids %}{{ id }}{% ifchanged id %}-{% cycle red,blue %}{% else %}{% endifchanged %},{% endfor %}', {'ids': [1,1,2,2,2,3]}, '1-red,1,2-blue,2,2,3-red,'),
 
639
 
 
640
            'ifchanged-else04': ('{% for id in ids %}{% ifchanged %}***{{ id }}*{% else %}...{% endifchanged %}{{ forloop.counter }}{% endfor %}', {'ids': [1,1,2,2,2,3,4]}, '***1*1...2***2*3...4...5***3*6***4*7'),
 
641
 
 
642
            ### IFEQUAL TAG ###########################################################
 
643
            'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
 
644
            'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"),
 
645
            'ifequal03': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 2}, "no"),
 
646
            'ifequal04': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 1}, "yes"),
 
647
            'ifequal05': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "test"}, "yes"),
 
648
            'ifequal06': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "no"}, "no"),
 
649
            'ifequal07': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "test"}, "yes"),
 
650
            'ifequal08': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "no"}, "no"),
 
651
            'ifequal09': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {}, "no"),
 
652
            'ifequal10': ('{% ifequal a b %}yes{% else %}no{% endifequal %}', {}, "yes"),
 
653
 
 
654
            # SMART SPLITTING
 
655
            'ifequal-split01': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {}, "no"),
 
656
            'ifequal-split02': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'foo'}, "no"),
 
657
            'ifequal-split03': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'test man'}, "yes"),
 
658
            'ifequal-split04': ("{% ifequal a 'test man' %}yes{% else %}no{% endifequal %}", {'a': 'test man'}, "yes"),
 
659
            'ifequal-split05': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': ''}, "no"),
 
660
            'ifequal-split06': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i "love" you'}, "yes"),
 
661
            'ifequal-split07': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i love you'}, "no"),
 
662
            'ifequal-split08': (r"{% ifequal a 'I\'m happy' %}yes{% else %}no{% endifequal %}", {'a': "I'm happy"}, "yes"),
 
663
            'ifequal-split09': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slash\man"}, "yes"),
 
664
            'ifequal-split10': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slashman"}, "no"),
 
665
 
 
666
            # NUMERIC RESOLUTION
 
667
            'ifequal-numeric01': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': '5'}, ''),
 
668
            'ifequal-numeric02': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
 
669
            'ifequal-numeric03': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5}, ''),
 
670
            'ifequal-numeric04': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5.2}, 'yes'),
 
671
            'ifequal-numeric05': ('{% ifequal x 0.2 %}yes{% endifequal %}', {'x': .2}, 'yes'),
 
672
            'ifequal-numeric06': ('{% ifequal x .2 %}yes{% endifequal %}', {'x': .2}, 'yes'),
 
673
            'ifequal-numeric07': ('{% ifequal x 2. %}yes{% endifequal %}', {'x': 2}, ''),
 
674
            'ifequal-numeric08': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': 5}, ''),
 
675
            'ifequal-numeric09': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': '5'}, 'yes'),
 
676
            'ifequal-numeric10': ('{% ifequal x -5 %}yes{% endifequal %}', {'x': -5}, 'yes'),
 
677
            'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, 'yes'),
 
678
            'ifequal-numeric12': ('{% ifequal x +5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
 
679
 
 
680
            # FILTER EXPRESSIONS AS ARGUMENTS
 
681
            'ifequal-filter01': ('{% ifequal a|upper "A" %}x{% endifequal %}', {'a': 'a'}, 'x'),
 
682
            'ifequal-filter02': ('{% ifequal "A" a|upper %}x{% endifequal %}', {'a': 'a'}, 'x'),
 
683
            'ifequal-filter03': ('{% ifequal a|upper b|upper %}x{% endifequal %}', {'a': 'x', 'b': 'X'}, 'x'),
 
684
            'ifequal-filter04': ('{% ifequal x|slice:"1" "a" %}x{% endifequal %}', {'x': 'aaa'}, 'x'),
 
685
            'ifequal-filter05': ('{% ifequal x|slice:"1"|upper "A" %}x{% endifequal %}', {'x': 'aaa'}, 'x'),
 
686
 
 
687
            ### IFNOTEQUAL TAG ########################################################
 
688
            'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
 
689
            'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""),
 
690
            'ifnotequal03': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
 
691
            'ifnotequal04': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 1}, "no"),
 
692
 
 
693
            ### INCLUDE TAG ###########################################################
 
694
            'include01': ('{% include "basic-syntax01" %}', {}, "something cool"),
 
695
            'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
 
696
            'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
 
697
            'include04': ('a{% include "nonexistent" %}b', {}, "ab"),
 
698
            'include 05': ('template with a space', {}, 'template with a space'),
 
699
            'include06': ('{% include "include 05"%}', {}, 'template with a space'),
 
700
 
 
701
            ### NAMED ENDBLOCKS #######################################################
 
702
 
 
703
            # Basic test
 
704
            'namedendblocks01': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock first %}3", {}, '1_2_3'),
 
705
 
 
706
            # Unbalanced blocks
 
707
            'namedendblocks02': ("1{% block first %}_{% block second %}2{% endblock first %}_{% endblock second %}3", {}, template.TemplateSyntaxError),
 
708
            'namedendblocks03': ("1{% block first %}_{% block second %}2{% endblock %}_{% endblock second %}3", {}, template.TemplateSyntaxError),
 
709
            'namedendblocks04': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock third %}3", {}, template.TemplateSyntaxError),
 
710
            'namedendblocks05': ("1{% block first %}_{% block second %}2{% endblock first %}", {}, template.TemplateSyntaxError),
 
711
 
 
712
            # Mixed named and unnamed endblocks
 
713
            'namedendblocks06': ("1{% block first %}_{% block second %}2{% endblock %}_{% endblock first %}3", {}, '1_2_3'),
 
714
            'namedendblocks07': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock %}3", {}, '1_2_3'),
 
715
 
 
716
            ### INHERITANCE ###########################################################
 
717
 
 
718
            # Standard template with no inheritance
 
719
            'inheritance01': ("1{% block first %}&{% endblock %}3{% block second %}_{% endblock %}", {}, '1&3_'),
 
720
 
 
721
            # Standard two-level inheritance
 
722
            'inheritance02': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'),
 
723
 
 
724
            # Three-level with no redefinitions on third level
 
725
            'inheritance03': ("{% extends 'inheritance02' %}", {}, '1234'),
 
726
 
 
727
            # Two-level with no redefinitions on second level
 
728
            'inheritance04': ("{% extends 'inheritance01' %}", {}, '1&3_'),
 
729
 
 
730
            # Two-level with double quotes instead of single quotes
 
731
            'inheritance05': ('{% extends "inheritance02" %}', {}, '1234'),
 
732
 
 
733
            # Three-level with variable parent-template name
 
734
            'inheritance06': ("{% extends foo %}", {'foo': 'inheritance02'}, '1234'),
 
735
 
 
736
            # Two-level with one block defined, one block not defined
 
737
            'inheritance07': ("{% extends 'inheritance01' %}{% block second %}5{% endblock %}", {}, '1&35'),
 
738
 
 
739
            # Three-level with one block defined on this level, two blocks defined next level
 
740
            'inheritance08': ("{% extends 'inheritance02' %}{% block second %}5{% endblock %}", {}, '1235'),
 
741
 
 
742
            # Three-level with second and third levels blank
 
743
            'inheritance09': ("{% extends 'inheritance04' %}", {}, '1&3_'),
 
744
 
 
745
            # Three-level with space NOT in a block -- should be ignored
 
746
            'inheritance10': ("{% extends 'inheritance04' %}      ", {}, '1&3_'),
 
747
 
 
748
            # Three-level with both blocks defined on this level, but none on second level
 
749
            'inheritance11': ("{% extends 'inheritance04' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'),
 
750
 
 
751
            # Three-level with this level providing one and second level providing the other
 
752
            'inheritance12': ("{% extends 'inheritance07' %}{% block first %}2{% endblock %}", {}, '1235'),
 
753
 
 
754
            # Three-level with this level overriding second level
 
755
            'inheritance13': ("{% extends 'inheritance02' %}{% block first %}a{% endblock %}{% block second %}b{% endblock %}", {}, '1a3b'),
 
756
 
 
757
            # A block defined only in a child template shouldn't be displayed
 
758
            'inheritance14': ("{% extends 'inheritance01' %}{% block newblock %}NO DISPLAY{% endblock %}", {}, '1&3_'),
 
759
 
 
760
            # A block within another block
 
761
            'inheritance15': ("{% extends 'inheritance01' %}{% block first %}2{% block inner %}inner{% endblock %}{% endblock %}", {}, '12inner3_'),
 
762
 
 
763
            # A block within another block (level 2)
 
764
            'inheritance16': ("{% extends 'inheritance15' %}{% block inner %}out{% endblock %}", {}, '12out3_'),
 
765
 
 
766
            # {% load %} tag (parent -- setup for exception04)
 
767
            'inheritance17': ("{% load testtags %}{% block first %}1234{% endblock %}", {}, '1234'),
 
768
 
 
769
            # {% load %} tag (standard usage, without inheritance)
 
770
            'inheritance18': ("{% load testtags %}{% echo this that theother %}5678", {}, 'this that theother5678'),
 
771
 
 
772
            # {% load %} tag (within a child template)
 
773
            'inheritance19': ("{% extends 'inheritance01' %}{% block first %}{% load testtags %}{% echo 400 %}5678{% endblock %}", {}, '140056783_'),
 
774
 
 
775
            # Two-level inheritance with {{ block.super }}
 
776
            'inheritance20': ("{% extends 'inheritance01' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1&a3_'),
 
777
 
 
778
            # Three-level inheritance with {{ block.super }} from parent
 
779
            'inheritance21': ("{% extends 'inheritance02' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '12a34'),
 
780
 
 
781
            # Three-level inheritance with {{ block.super }} from grandparent
 
782
            'inheritance22': ("{% extends 'inheritance04' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1&a3_'),
 
783
 
 
784
            # Three-level inheritance with {{ block.super }} from parent and grandparent
 
785
            'inheritance23': ("{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", {}, '1&ab3_'),
 
786
 
 
787
            # Inheritance from local context without use of template loader
 
788
            'inheritance24': ("{% extends context_template %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")}, '1234'),
 
789
 
 
790
            # Inheritance from local context with variable parent template
 
791
            'inheritance25': ("{% extends context_template.1 %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': [template.Template("Wrong"), template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")]}, '1234'),
 
792
 
 
793
            # Set up a base template to extend
 
794
            'inheritance26': ("no tags", {}, 'no tags'),
 
795
 
 
796
            # Inheritance from a template that doesn't have any blocks
 
797
            'inheritance27': ("{% extends 'inheritance26' %}", {}, 'no tags'),
 
798
 
 
799
            # Set up a base template with a space in it.
 
800
            'inheritance 28': ("{% block first %}!{% endblock %}", {}, '!'),
 
801
 
 
802
            # Inheritance from a template with a space in its name should work.
 
803
            'inheritance29': ("{% extends 'inheritance 28' %}", {}, '!'),
 
804
 
 
805
            ### I18N ##################################################################
 
806
 
 
807
            # {% spaceless %} tag
 
808
            'spaceless01': ("{% spaceless %} <b>    <i> text </i>    </b> {% endspaceless %}", {}, "<b><i> text </i></b>"),
 
809
            'spaceless02': ("{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}", {}, "<b><i> text </i></b>"),
 
810
            'spaceless03': ("{% spaceless %}<b><i>text</i></b>{% endspaceless %}", {}, "<b><i>text</i></b>"),
 
811
 
 
812
            # simple translation of a string delimited by '
 
813
            'i18n01': ("{% load i18n %}{% trans 'xxxyyyxxx' %}", {}, "xxxyyyxxx"),
 
814
 
 
815
            # simple translation of a string delimited by "
 
816
            'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"),
 
817
 
 
818
            # simple translation of a variable
 
819
            'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u"Å"),
 
820
 
 
821
            # simple translation of a variable and filter
 
822
            'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'),
 
823
 
 
824
            # simple translation of a string with interpolation
 
825
            'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"),
 
826
 
 
827
            # simple translation of a string to german
 
828
            'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"),
 
829
 
 
830
            # translation of singular form
 
831
            'i18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"),
 
832
 
 
833
            # translation of plural form
 
834
            'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"),
 
835
 
 
836
            # simple non-translation (only marking) of a string to german
 
837
            'i18n09': ('{% load i18n %}{% trans "Page not found" noop %}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
 
838
 
 
839
            # translation of a variable with a translated filter
 
840
            'i18n10': ('{{ bool|yesno:_("yes,no,maybe") }}', {'bool': True, 'LANGUAGE_CODE': 'de'}, 'Ja'),
 
841
 
 
842
            # translation of a variable with a non-translated filter
 
843
            'i18n11': ('{{ bool|yesno:"ja,nein" }}', {'bool': True}, 'ja'),
 
844
 
 
845
            # usage of the get_available_languages tag
 
846
            'i18n12': ('{% load i18n %}{% get_available_languages as langs %}{% for lang in langs %}{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}', {}, 'de'),
 
847
 
 
848
            # translation of constant strings
 
849
            'i18n13': ('{{ _("Password") }}', {'LANGUAGE_CODE': 'de'}, 'Passwort'),
 
850
            'i18n14': ('{% cycle "foo" _("Password") _(\'Password\') as c %} {% cycle c %} {% cycle c %}', {'LANGUAGE_CODE': 'de'}, 'foo Passwort Passwort'),
 
851
            'i18n15': ('{{ absent|default:_("Password") }}', {'LANGUAGE_CODE': 'de', 'absent': ""}, 'Passwort'),
 
852
            'i18n16': ('{{ _("<") }}', {'LANGUAGE_CODE': 'de'}, '<'),
 
853
 
 
854
            # Escaping inside blocktrans and trans works as if it was directly in the
 
855
            # template.
 
856
            'i18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
 
857
            'i18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α &amp; β'),
 
858
            'i18n19': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': 'a & b'}, u'a &amp; b'),
 
859
            'i18n20': ('{% load i18n %}{% trans andrew %}', {'andrew': 'a & b'}, u'a &amp; b'),
 
860
            'i18n21': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': mark_safe('a & b')}, u'a & b'),
 
861
            'i18n22': ('{% load i18n %}{% trans andrew %}', {'andrew': mark_safe('a & b')}, u'a & b'),
 
862
 
 
863
            ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
 
864
 
 
865
            'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
 
866
            'invalidstr02': ('{{ var|default_if_none:"Foo" }}', {}, ('','INVALID')),
 
867
            'invalidstr03': ('{% for v in var %}({{ v }}){% endfor %}', {}, ''),
 
868
            'invalidstr04': ('{% if var %}Yes{% else %}No{% endif %}', {}, 'No'),
 
869
            'invalidstr04': ('{% if var|default:"Foo" %}Yes{% else %}No{% endif %}', {}, 'Yes'),
 
870
            'invalidstr05': ('{{ var }}', {}, ('', 'INVALID %s', 'var')),
 
871
            'invalidstr06': ('{{ var.prop }}', {'var': {}}, ('', 'INVALID %s', 'var.prop')),
 
872
 
 
873
            ### MULTILINE #############################################################
 
874
 
 
875
            'multiline01': ("""
 
876
                            Hello,
 
877
                            boys.
 
878
                            How
 
879
                            are
 
880
                            you
 
881
                            gentlemen.
 
882
                            """,
 
883
                            {},
 
884
                            """
 
885
                            Hello,
 
886
                            boys.
 
887
                            How
 
888
                            are
 
889
                            you
 
890
                            gentlemen.
 
891
                            """),
 
892
 
 
893
            ### REGROUP TAG ###########################################################
 
894
            'regroup01': ('{% regroup data by bar as grouped %}' + \
 
895
                          '{% for group in grouped %}' + \
 
896
                          '{{ group.grouper }}:' + \
 
897
                          '{% for item in group.list %}' + \
 
898
                          '{{ item.foo }}' + \
 
899
                          '{% endfor %},' + \
 
900
                          '{% endfor %}',
 
901
                          {'data': [ {'foo':'c', 'bar':1},
 
902
                                     {'foo':'d', 'bar':1},
 
903
                                     {'foo':'a', 'bar':2},
 
904
                                     {'foo':'b', 'bar':2},
 
905
                                     {'foo':'x', 'bar':3}  ]},
 
906
                          '1:cd,2:ab,3:x,'),
 
907
 
 
908
            # Test for silent failure when target variable isn't found
 
909
            'regroup02': ('{% regroup data by bar as grouped %}' + \
 
910
                          '{% for group in grouped %}' + \
 
911
                          '{{ group.grouper }}:' + \
 
912
                          '{% for item in group.list %}' + \
 
913
                          '{{ item.foo }}' + \
 
914
                          '{% endfor %},' + \
 
915
                          '{% endfor %}',
 
916
                          {}, ''),
 
917
 
 
918
            ### TEMPLATETAG TAG #######################################################
 
919
            'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
 
920
            'templatetag02': ('{% templatetag closeblock %}', {}, '%}'),
 
921
            'templatetag03': ('{% templatetag openvariable %}', {}, '{{'),
 
922
            'templatetag04': ('{% templatetag closevariable %}', {}, '}}'),
 
923
            'templatetag05': ('{% templatetag %}', {}, template.TemplateSyntaxError),
 
924
            'templatetag06': ('{% templatetag foo %}', {}, template.TemplateSyntaxError),
 
925
            'templatetag07': ('{% templatetag openbrace %}', {}, '{'),
 
926
            'templatetag08': ('{% templatetag closebrace %}', {}, '}'),
 
927
            'templatetag09': ('{% templatetag openbrace %}{% templatetag openbrace %}', {}, '{{'),
 
928
            'templatetag10': ('{% templatetag closebrace %}{% templatetag closebrace %}', {}, '}}'),
 
929
            'templatetag11': ('{% templatetag opencomment %}', {}, '{#'),
 
930
            'templatetag12': ('{% templatetag closecomment %}', {}, '#}'),
 
931
 
 
932
            ### WIDTHRATIO TAG ########################################################
 
933
            'widthratio01': ('{% widthratio a b 0 %}', {'a':50,'b':100}, '0'),
 
934
            'widthratio02': ('{% widthratio a b 100 %}', {'a':0,'b':0}, ''),
 
935
            'widthratio03': ('{% widthratio a b 100 %}', {'a':0,'b':100}, '0'),
 
936
            'widthratio04': ('{% widthratio a b 100 %}', {'a':50,'b':100}, '50'),
 
937
            'widthratio05': ('{% widthratio a b 100 %}', {'a':100,'b':100}, '100'),
 
938
 
 
939
            # 62.5 should round to 63
 
940
            'widthratio06': ('{% widthratio a b 100 %}', {'a':50,'b':80}, '63'),
 
941
 
 
942
            # 71.4 should round to 71
 
943
            'widthratio07': ('{% widthratio a b 100 %}', {'a':50,'b':70}, '71'),
 
944
 
 
945
            # Raise exception if we don't have 3 args, last one an integer
 
946
            'widthratio08': ('{% widthratio %}', {}, template.TemplateSyntaxError),
 
947
            'widthratio09': ('{% widthratio a b %}', {'a':50,'b':100}, template.TemplateSyntaxError),
 
948
            'widthratio10': ('{% widthratio a b 100.0 %}', {'a':50,'b':100}, '50'),
 
949
 
 
950
            # #10043: widthratio should allow max_width to be a variable
 
951
            'widthratio11': ('{% widthratio a b c %}', {'a':50,'b':100, 'c': 100}, '50'),
 
952
 
 
953
            ### WITH TAG ########################################################
 
954
            'with01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, '50'),
 
955
            'with02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key':50}}, ('50-50-50', 'INVALID50-50-50INVALID')),
 
956
 
 
957
            'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
 
958
            'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key':50}}, template.TemplateSyntaxError),
 
959
 
 
960
            ### NOW TAG ########################################################
 
961
            # Simple case
 
962
            'now01': ('{% now "j n Y"%}', {}, str(datetime.now().day) + ' ' + str(datetime.now().month) + ' ' + str(datetime.now().year)),
 
963
 
 
964
            # Check parsing of escaped and special characters
 
965
            'now02': ('{% now "j "n" Y"%}', {}, template.TemplateSyntaxError),
 
966
        #    'now03': ('{% now "j \"n\" Y"%}', {}, str(datetime.now().day) + '"' + str(datetime.now().month) + '"' + str(datetime.now().year)),
 
967
        #    'now04': ('{% now "j \nn\n Y"%}', {}, str(datetime.now().day) + '\n' + str(datetime.now().month) + '\n' + str(datetime.now().year))
 
968
 
 
969
            ### URL TAG ########################################################
 
970
            # Successes
 
971
            'url01': ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
 
972
            'url02': ('{% url regressiontests.templates.views.client_action id=client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
 
973
            'url02a': ('{% url regressiontests.templates.views.client_action client.id,"update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
 
974
            'url03': ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
 
975
            'url04': ('{% url named.client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
 
976
            'url05': (u'{% url метка_оператора v %}', {'v': u'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
 
977
            'url06': (u'{% url метка_оператора_2 tag=v %}', {'v': u'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
 
978
            'url07': (u'{% url regressiontests.templates.views.client2 tag=v %}', {'v': u'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
 
979
            'url08': (u'{% url метка_оператора v %}', {'v': 'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
 
980
            'url09': (u'{% url метка_оператора_2 tag=v %}', {'v': 'Ω'}, '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
 
981
            'url10': ('{% url regressiontests.templates.views.client_action id=client.id,action="two words" %}', {'client': {'id': 1}}, '/url_tag/client/1/two%20words/'),
 
982
 
 
983
            # Failures
 
984
            'url-fail01': ('{% url %}', {}, template.TemplateSyntaxError),
 
985
            'url-fail02': ('{% url no_such_view %}', {}, urlresolvers.NoReverseMatch),
 
986
            'url-fail03': ('{% url regressiontests.templates.views.client %}', {}, urlresolvers.NoReverseMatch),
 
987
 
 
988
            # {% url ... as var %}
 
989
            'url-asvar01': ('{% url regressiontests.templates.views.index as url %}', {}, ''),
 
990
            'url-asvar02': ('{% url regressiontests.templates.views.index as url %}{{ url }}', {}, '/url_tag/'),
 
991
            'url-asvar03': ('{% url no_such_view as url %}{{ url }}', {}, ''),
 
992
 
 
993
            ### CACHE TAG ######################################################
 
994
            'cache01': ('{% load cache %}{% cache -1 test %}cache01{% endcache %}', {}, 'cache01'),
 
995
            'cache02': ('{% load cache %}{% cache -1 test %}cache02{% endcache %}', {}, 'cache02'),
 
996
            'cache03': ('{% load cache %}{% cache 2 test %}cache03{% endcache %}', {}, 'cache03'),
 
997
            'cache04': ('{% load cache %}{% cache 2 test %}cache04{% endcache %}', {}, 'cache03'),
 
998
            'cache05': ('{% load cache %}{% cache 2 test foo %}cache05{% endcache %}', {'foo': 1}, 'cache05'),
 
999
            'cache06': ('{% load cache %}{% cache 2 test foo %}cache06{% endcache %}', {'foo': 2}, 'cache06'),
 
1000
            'cache07': ('{% load cache %}{% cache 2 test foo %}cache07{% endcache %}', {'foo': 1}, 'cache05'),
 
1001
 
 
1002
            # Allow first argument to be a variable.
 
1003
            'cache08': ('{% load cache %}{% cache time test foo %}cache08{% endcache %}', {'foo': 2, 'time': 2}, 'cache06'),
 
1004
            'cache09': ('{% load cache %}{% cache time test foo %}cache09{% endcache %}', {'foo': 3, 'time': -1}, 'cache09'),
 
1005
            'cache10': ('{% load cache %}{% cache time test foo %}cache10{% endcache %}', {'foo': 3, 'time': -1}, 'cache10'),
 
1006
 
 
1007
            # Raise exception if we don't have at least 2 args, first one integer.
 
1008
            'cache11': ('{% load cache %}{% cache %}{% endcache %}', {}, template.TemplateSyntaxError),
 
1009
            'cache12': ('{% load cache %}{% cache 1 %}{% endcache %}', {}, template.TemplateSyntaxError),
 
1010
            'cache13': ('{% load cache %}{% cache foo bar %}{% endcache %}', {}, template.TemplateSyntaxError),
 
1011
            'cache14': ('{% load cache %}{% cache foo bar %}{% endcache %}', {'foo': 'fail'}, template.TemplateSyntaxError),
 
1012
            'cache15': ('{% load cache %}{% cache foo bar %}{% endcache %}', {'foo': []}, template.TemplateSyntaxError),
 
1013
 
 
1014
            # Regression test for #7460.
 
1015
            'cache16': ('{% load cache %}{% cache 1 foo bar %}{% endcache %}', {'foo': 'foo', 'bar': 'with spaces'}, ''),
 
1016
 
 
1017
            # Regression test for #11270.
 
1018
            'cache17': ('{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}', {'poem': 'Oh freddled gruntbuggly/Thy micturations are to me/As plurdled gabbleblotchits/On a lurgid bee/That mordiously hath bitled out/Its earted jurtles/Into a rancid festering/Or else I shall rend thee in the gobberwarts with my blurglecruncheon/See if I dont.'}, 'Some Content'),
 
1019
 
 
1020
            ### AUTOESCAPE TAG ##############################################
 
1021
            'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"),
 
1022
            'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "<b>hello</b>"),
 
1023
            'autoescape-tag03': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "<b>hello</b>"}, "&lt;b&gt;hello&lt;/b&gt;"),
 
1024
 
 
1025
            # Autoescape disabling and enabling nest in a predictable way.
 
1026
            'autoescape-tag04': ("{% autoescape off %}{{ first }} {% autoescape  on%}{{ first }}{% endautoescape %}{% endautoescape %}", {"first": "<a>"}, "<a> &lt;a&gt;"),
 
1027
 
 
1028
            'autoescape-tag05': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "<b>first</b>"}, "&lt;b&gt;first&lt;/b&gt;"),
 
1029
 
 
1030
            # Strings (ASCII or unicode) already marked as "safe" are not
 
1031
            # auto-escaped
 
1032
            'autoescape-tag06': ("{{ first }}", {"first": mark_safe("<b>first</b>")}, "<b>first</b>"),
 
1033
            'autoescape-tag07': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": mark_safe(u"<b>Apple</b>")}, u"<b>Apple</b>"),
 
1034
 
 
1035
            # Literal string arguments to filters, if used in the result, are
 
1036
            # safe.
 
1037
            'autoescape-tag08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'),
 
1038
 
 
1039
            # Objects which return safe strings as their __unicode__ method
 
1040
            # won't get double-escaped.
 
1041
            'autoescape-tag09': (r'{{ unsafe }}', {'unsafe': filters.UnsafeClass()}, 'you &amp; me'),
 
1042
            'autoescape-tag10': (r'{{ safe }}', {'safe': filters.SafeClass()}, 'you &gt; me'),
 
1043
 
 
1044
            # The "safe" and "escape" filters cannot work due to internal
 
1045
            # implementation details (fortunately, the (no)autoescape block
 
1046
            # tags can be used in those cases)
 
1047
            'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}", {"first": "<a>"}, template.TemplateSyntaxError),
 
1048
        }
 
1049
 
 
1050
if __name__ == "__main__":
 
1051
    unittest.main()