thirdparty/google_appengine/lib/django/tests/regressiontests/templates/tests.py
changeset 2866 a04b1e4126c4
parent 2864 2e0b0af889be
child 2868 9f7f269383f7
equal deleted inserted replaced
2864:2e0b0af889be 2866:a04b1e4126c4
     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 django import template
       
    10 from django.template import loader
       
    11 from django.utils.translation import activate, deactivate, install
       
    12 from django.utils.tzinfo import LocalTimezone
       
    13 from datetime import datetime, timedelta
       
    14 import unittest
       
    15 
       
    16 #################################
       
    17 # Custom template tag for tests #
       
    18 #################################
       
    19 
       
    20 register = template.Library()
       
    21 
       
    22 class EchoNode(template.Node):
       
    23     def __init__(self, contents):
       
    24         self.contents = contents
       
    25 
       
    26     def render(self, context):
       
    27         return " ".join(self.contents)
       
    28 
       
    29 def do_echo(parser, token):
       
    30     return EchoNode(token.contents.split()[1:])
       
    31 
       
    32 register.tag("echo", do_echo)
       
    33 
       
    34 template.libraries['django.templatetags.testtags'] = register
       
    35 
       
    36 #####################################
       
    37 # Helper objects for template tests #
       
    38 #####################################
       
    39 
       
    40 class SomeException(Exception):
       
    41     silent_variable_failure = True
       
    42 
       
    43 class SomeOtherException(Exception):
       
    44     pass
       
    45 
       
    46 class SomeClass:
       
    47     def __init__(self):
       
    48         self.otherclass = OtherClass()
       
    49 
       
    50     def method(self):
       
    51         return "SomeClass.method"
       
    52 
       
    53     def method2(self, o):
       
    54         return o
       
    55 
       
    56     def method3(self):
       
    57         raise SomeException
       
    58 
       
    59     def method4(self):
       
    60         raise SomeOtherException
       
    61 
       
    62 class OtherClass:
       
    63     def method(self):
       
    64         return "OtherClass.method"
       
    65 
       
    66 class UnicodeInStrClass:
       
    67     "Class whose __str__ returns a Unicode object."
       
    68     def __str__(self):
       
    69         return u'ŠĐĆŽćžšđ'
       
    70 
       
    71 class Templates(unittest.TestCase):
       
    72     def test_templates(self):
       
    73         # NOW and NOW_tz are used by timesince tag tests.
       
    74         NOW = datetime.now()
       
    75         NOW_tz = datetime.now(LocalTimezone(datetime.now()))
       
    76 
       
    77         # SYNTAX --
       
    78         # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class)
       
    79         TEMPLATE_TESTS = {
       
    80 
       
    81             ### BASIC SYNTAX ##########################################################
       
    82 
       
    83             # Plain text should go through the template parser untouched
       
    84             'basic-syntax01': ("something cool", {}, "something cool"),
       
    85 
       
    86             # Variables should be replaced with their value in the current context
       
    87             'basic-syntax02': ("{{ headline }}", {'headline':'Success'}, "Success"),
       
    88 
       
    89             # More than one replacement variable is allowed in a template
       
    90             'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
       
    91 
       
    92             # Fail silently when a variable is not found in the current context
       
    93             'basic-syntax04': ("as{{ missing }}df", {}, ("asdf","asINVALIDdf")),
       
    94 
       
    95             # A variable may not contain more than one word
       
    96             'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
       
    97 
       
    98             # Raise TemplateSyntaxError for empty variable tags
       
    99             'basic-syntax07': ("{{ }}",        {}, template.TemplateSyntaxError),
       
   100             'basic-syntax08': ("{{        }}", {}, template.TemplateSyntaxError),
       
   101 
       
   102             # Attribute syntax allows a template to call an object's attribute
       
   103             'basic-syntax09': ("{{ var.method }}", {"var": SomeClass()}, "SomeClass.method"),
       
   104 
       
   105             # Multiple levels of attribute access are allowed
       
   106             'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
       
   107 
       
   108             # Fail silently when a variable's attribute isn't found
       
   109             'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, ("","INVALID")),
       
   110 
       
   111             # Raise TemplateSyntaxError when trying to access a variable beginning with an underscore
       
   112             'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
       
   113 
       
   114             # Raise TemplateSyntaxError when trying to access a variable containing an illegal character
       
   115             'basic-syntax13': ("{{ va>r }}", {}, template.TemplateSyntaxError),
       
   116             'basic-syntax14': ("{{ (var.r) }}", {}, template.TemplateSyntaxError),
       
   117             'basic-syntax15': ("{{ sp%am }}", {}, template.TemplateSyntaxError),
       
   118             'basic-syntax16': ("{{ eggs! }}", {}, template.TemplateSyntaxError),
       
   119             'basic-syntax17': ("{{ moo? }}", {}, template.TemplateSyntaxError),
       
   120 
       
   121             # Attribute syntax allows a template to call a dictionary key's value
       
   122             'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
       
   123 
       
   124             # Fail silently when a variable's dictionary key isn't found
       
   125             'basic-syntax19': ("{{ foo.spam }}", {"foo" : {"bar" : "baz"}}, ("","INVALID")),
       
   126 
       
   127             # Fail silently when accessing a non-simple method
       
   128             'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("","INVALID")),
       
   129 
       
   130             # List-index syntax allows a template to access a certain item of a subscriptable object.
       
   131             'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"),
       
   132 
       
   133             # Fail silently when the list index is out of range.
       
   134             'list-index02': ("{{ var.5 }}", {"var": ["first item", "second item"]}, ("", "INVALID")),
       
   135 
       
   136             # Fail silently when the variable is not a subscriptable object.
       
   137             'list-index03': ("{{ var.1 }}", {"var": None}, ("", "INVALID")),
       
   138 
       
   139             # Fail silently when variable is a dict without the specified key.
       
   140             'list-index04': ("{{ var.1 }}", {"var": {}}, ("", "INVALID")),
       
   141 
       
   142             # Dictionary lookup wins out when dict's key is a string.
       
   143             'list-index05': ("{{ var.1 }}", {"var": {'1': "hello"}}, "hello"),
       
   144 
       
   145             # But list-index lookup wins out when dict's key is an int, which
       
   146             # behind the scenes is really a dictionary lookup (for a dict)
       
   147             # after converting the key to an int.
       
   148             'list-index06': ("{{ var.1 }}", {"var": {1: "hello"}}, "hello"),
       
   149 
       
   150             # Dictionary lookup wins out when there is a string and int version of the key.
       
   151             'list-index07': ("{{ var.1 }}", {"var": {'1': "hello", 1: "world"}}, "hello"),
       
   152             
       
   153             # Basic filter usage
       
   154             'basic-syntax21': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"),
       
   155 
       
   156             # Chained filters
       
   157             'basic-syntax22': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"),
       
   158 
       
   159             # Raise TemplateSyntaxError for space between a variable and filter pipe
       
   160             'basic-syntax23': ("{{ var |upper }}", {}, template.TemplateSyntaxError),
       
   161 
       
   162             # Raise TemplateSyntaxError for space after a filter pipe
       
   163             'basic-syntax24': ("{{ var| upper }}", {}, template.TemplateSyntaxError),
       
   164 
       
   165             # Raise TemplateSyntaxError for a nonexistent filter
       
   166             'basic-syntax25': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError),
       
   167 
       
   168             # Raise TemplateSyntaxError when trying to access a filter containing an illegal character
       
   169             'basic-syntax26': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError),
       
   170 
       
   171             # Raise TemplateSyntaxError for invalid block tags
       
   172             'basic-syntax27': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError),
       
   173 
       
   174             # Raise TemplateSyntaxError for empty block tags
       
   175             'basic-syntax28': ("{% %}", {}, template.TemplateSyntaxError),
       
   176 
       
   177             # Chained filters, with an argument to the first one
       
   178             'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
       
   179 
       
   180             # Escaped string as argument
       
   181             'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'),
       
   182 
       
   183             # Variable as argument
       
   184             'basic-syntax31': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'),
       
   185 
       
   186             # Default argument testing
       
   187             'basic-syntax32': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
       
   188 
       
   189             # Fail silently for methods that raise an exception with a "silent_variable_failure" attribute
       
   190             'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")),
       
   191 
       
   192             # In methods that raise an exception without a "silent_variable_attribute" set to True,
       
   193             # the exception propagates
       
   194             'basic-syntax34': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException),
       
   195 
       
   196             # Escaped backslash in argument
       
   197             'basic-syntax35': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'),
       
   198 
       
   199             # Escaped backslash using known escape char
       
   200             'basic-syntax35': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'),
       
   201 
       
   202             # Empty strings can be passed as arguments to filters
       
   203             'basic-syntax36': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
       
   204 
       
   205             # If a variable has a __str__() that returns a Unicode object, the value
       
   206             # will be converted to a bytestring.
       
   207             'basic-syntax37': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'),
       
   208 
       
   209             ### COMMENT SYNTAX ########################################################
       
   210             'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"),
       
   211             'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"),
       
   212 
       
   213             # Comments can contain invalid stuff.
       
   214             'comment-syntax03': ("foo{#  {% if %}  #}", {}, "foo"),
       
   215             'comment-syntax04': ("foo{#  {% endblock %}  #}", {}, "foo"),
       
   216             'comment-syntax05': ("foo{#  {% somerandomtag %}  #}", {}, "foo"),
       
   217             'comment-syntax06': ("foo{# {% #}", {}, "foo"),
       
   218             'comment-syntax07': ("foo{# %} #}", {}, "foo"),
       
   219             'comment-syntax08': ("foo{# %} #}bar", {}, "foobar"),
       
   220             'comment-syntax09': ("foo{# {{ #}", {}, "foo"),
       
   221             'comment-syntax10': ("foo{# }} #}", {}, "foo"),
       
   222             'comment-syntax11': ("foo{# { #}", {}, "foo"),
       
   223             'comment-syntax12': ("foo{# } #}", {}, "foo"),
       
   224 
       
   225             ### COMMENT TAG ###########################################################
       
   226             'comment-tag01': ("{% comment %}this is hidden{% endcomment %}hello", {}, "hello"),
       
   227             'comment-tag02': ("{% comment %}this is hidden{% endcomment %}hello{% comment %}foo{% endcomment %}", {}, "hello"),
       
   228 
       
   229             # Comment tag can contain invalid stuff.
       
   230             'comment-tag03': ("foo{% comment %} {% if %} {% endcomment %}", {}, "foo"),
       
   231             'comment-tag04': ("foo{% comment %} {% endblock %} {% endcomment %}", {}, "foo"),
       
   232             'comment-tag05': ("foo{% comment %} {% somerandomtag %} {% endcomment %}", {}, "foo"),
       
   233 
       
   234             ### CYCLE TAG #############################################################
       
   235             'cycle01': ('{% cycle a %}', {}, template.TemplateSyntaxError),
       
   236             'cycle02': ('{% cycle a,b,c as abc %}{% cycle abc %}', {}, 'ab'),
       
   237             'cycle03': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}', {}, 'abc'),
       
   238             'cycle04': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}', {}, 'abca'),
       
   239             'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
       
   240             'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
       
   241             'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
       
   242             'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
       
   243 
       
   244             ### EXCEPTIONS ############################################################
       
   245 
       
   246             # Raise exception for invalid template name
       
   247             'exception01': ("{% extends 'nonexistent' %}", {}, template.TemplateSyntaxError),
       
   248 
       
   249             # Raise exception for invalid template name (in variable)
       
   250             'exception02': ("{% extends nonexistent %}", {}, template.TemplateSyntaxError),
       
   251 
       
   252             # Raise exception for extra {% extends %} tags
       
   253             'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError),
       
   254 
       
   255             # Raise exception for custom tags used in child with {% load %} tag in parent, not in child
       
   256             'exception04': ("{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}", {}, template.TemplateSyntaxError),
       
   257 
       
   258             ### FILTER TAG ############################################################
       
   259             'filter01': ('{% filter upper %}{% endfilter %}', {}, ''),
       
   260             'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'),
       
   261             'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'),
       
   262 
       
   263             ### FIRSTOF TAG ###########################################################
       
   264             'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''),
       
   265             'firstof02': ('{% firstof a b c %}', {'a':1,'b':0,'c':0}, '1'),
       
   266             'firstof03': ('{% firstof a b c %}', {'a':0,'b':2,'c':0}, '2'),
       
   267             'firstof04': ('{% firstof a b c %}', {'a':0,'b':0,'c':3}, '3'),
       
   268             'firstof05': ('{% firstof a b c %}', {'a':1,'b':2,'c':3}, '1'),
       
   269             'firstof06': ('{% firstof %}', {}, template.TemplateSyntaxError),
       
   270 
       
   271             ### FOR TAG ###############################################################
       
   272             'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"),
       
   273             'for-tag02': ("{% for val in values reversed %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "321"),
       
   274             'for-tag-vars01': ("{% for val in values %}{{ forloop.counter }}{% endfor %}", {"values": [6, 6, 6]}, "123"),
       
   275             'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"),
       
   276             'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"),
       
   277             'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"),
       
   278 
       
   279             ### IF TAG ################################################################
       
   280             'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
       
   281             'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
       
   282             'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
       
   283 
       
   284             # AND
       
   285             'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
       
   286             'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
       
   287             'if-tag-and03': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
       
   288             'if-tag-and04': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
       
   289             'if-tag-and05': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
       
   290             'if-tag-and06': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
       
   291             'if-tag-and07': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
       
   292             'if-tag-and08': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': True}, 'no'),
       
   293 
       
   294             # OR
       
   295             'if-tag-or01': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
       
   296             'if-tag-or02': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
       
   297             'if-tag-or03': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
       
   298             'if-tag-or04': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
       
   299             'if-tag-or05': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
       
   300             'if-tag-or06': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
       
   301             'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
       
   302             'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
       
   303 
       
   304             # TODO: multiple ORs
       
   305 
       
   306             # NOT
       
   307             'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'),
       
   308             'if-tag-not02': ("{% if not %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
       
   309             'if-tag-not03': ("{% if not %}yes{% else %}no{% endif %}", {'not': True}, 'yes'),
       
   310             'if-tag-not04': ("{% if not not %}no{% else %}yes{% endif %}", {'not': True}, 'yes'),
       
   311             'if-tag-not05': ("{% if not not %}no{% else %}yes{% endif %}", {}, 'no'),
       
   312 
       
   313             'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'),
       
   314             'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
       
   315             'if-tag-not08': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
       
   316             'if-tag-not09': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
       
   317             'if-tag-not10': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
       
   318 
       
   319             'if-tag-not11': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {}, 'no'),
       
   320             'if-tag-not12': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
       
   321             'if-tag-not13': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
       
   322             'if-tag-not14': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
       
   323             'if-tag-not15': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
       
   324 
       
   325             'if-tag-not16': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
       
   326             'if-tag-not17': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
       
   327             'if-tag-not18': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
       
   328             'if-tag-not19': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
       
   329             'if-tag-not20': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
       
   330 
       
   331             'if-tag-not21': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {}, 'yes'),
       
   332             'if-tag-not22': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
       
   333             'if-tag-not23': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
       
   334             'if-tag-not24': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
       
   335             'if-tag-not25': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
       
   336 
       
   337             'if-tag-not26': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
       
   338             'if-tag-not27': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
       
   339             'if-tag-not28': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
       
   340             'if-tag-not29': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
       
   341             'if-tag-not30': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
       
   342 
       
   343             'if-tag-not31': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
       
   344             'if-tag-not32': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
       
   345             'if-tag-not33': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
       
   346             'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
       
   347             'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
       
   348 
       
   349             # AND and OR raises a TemplateSyntaxError
       
   350             'if-tag-error01': ("{% if foo or bar and baz %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, template.TemplateSyntaxError),
       
   351             'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
       
   352             'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
       
   353             'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
       
   354             'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
       
   355 
       
   356             ### IFCHANGED TAG #########################################################
       
   357             'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,2,3) }, '123'),
       
   358             'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,3) }, '13'),
       
   359             'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,1) }, '1'),
       
   360             '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'),
       
   361             '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'),
       
   362             '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'),
       
   363             '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'),
       
   364 
       
   365             # Test one parameter given to ifchanged.
       
   366             'ifchanged-param01': ('{% for n in num %}{% ifchanged n %}..{% endifchanged %}{{ n }}{% endfor %}', { 'num': (1,2,3) }, '..1..2..3'),
       
   367             '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'),
       
   368 
       
   369             # Test multiple parameters to ifchanged.
       
   370             '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'),
       
   371 
       
   372             # Test a date+hour like construct, where the hour of the last day
       
   373             # is the same but the date had changed, so print the hour anyway.
       
   374             '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'),
       
   375 
       
   376             # Logically the same as above, just written with explicit
       
   377             # ifchanged for the day.
       
   378             'ifchanged-param04': ('{% 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'),
       
   379 
       
   380             ### IFEQUAL TAG ###########################################################
       
   381             'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
       
   382             'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"),
       
   383             'ifequal03': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 2}, "no"),
       
   384             'ifequal04': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 1}, "yes"),
       
   385             'ifequal05': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "test"}, "yes"),
       
   386             'ifequal06': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "no"}, "no"),
       
   387             'ifequal07': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "test"}, "yes"),
       
   388             'ifequal08': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "no"}, "no"),
       
   389             'ifequal09': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {}, "no"),
       
   390             'ifequal10': ('{% ifequal a b %}yes{% else %}no{% endifequal %}', {}, "yes"),
       
   391 
       
   392             # SMART SPLITTING
       
   393             'ifequal-split01': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {}, "no"),
       
   394             'ifequal-split02': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'foo'}, "no"),
       
   395             'ifequal-split03': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'test man'}, "yes"),
       
   396             'ifequal-split04': ("{% ifequal a 'test man' %}yes{% else %}no{% endifequal %}", {'a': 'test man'}, "yes"),
       
   397             'ifequal-split05': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': ''}, "no"),
       
   398             'ifequal-split06': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i "love" you'}, "yes"),
       
   399             'ifequal-split07': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i love you'}, "no"),
       
   400             'ifequal-split08': (r"{% ifequal a 'I\'m happy' %}yes{% else %}no{% endifequal %}", {'a': "I'm happy"}, "yes"),
       
   401             'ifequal-split09': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slash\man"}, "yes"),
       
   402             'ifequal-split10': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slashman"}, "no"),
       
   403 
       
   404             # NUMERIC RESOLUTION
       
   405             'ifequal-numeric01': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': '5'}, ''),
       
   406             'ifequal-numeric02': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
       
   407             'ifequal-numeric03': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5}, ''),
       
   408             'ifequal-numeric04': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5.2}, 'yes'),
       
   409             'ifequal-numeric05': ('{% ifequal x 0.2 %}yes{% endifequal %}', {'x': .2}, 'yes'),
       
   410             'ifequal-numeric06': ('{% ifequal x .2 %}yes{% endifequal %}', {'x': .2}, 'yes'),
       
   411             'ifequal-numeric07': ('{% ifequal x 2. %}yes{% endifequal %}', {'x': 2}, ''),
       
   412             'ifequal-numeric08': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': 5}, ''),
       
   413             'ifequal-numeric09': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': '5'}, 'yes'),
       
   414             'ifequal-numeric10': ('{% ifequal x -5 %}yes{% endifequal %}', {'x': -5}, 'yes'),
       
   415             'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, 'yes'),
       
   416             'ifequal-numeric12': ('{% ifequal x +5 %}yes{% endifequal %}', {'x': 5}, 'yes'),
       
   417 
       
   418             ### IFNOTEQUAL TAG ########################################################
       
   419             'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
       
   420             'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""),
       
   421             'ifnotequal03': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
       
   422             'ifnotequal04': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 1}, "no"),
       
   423 
       
   424             ### INCLUDE TAG ###########################################################
       
   425             'include01': ('{% include "basic-syntax01" %}', {}, "something cool"),
       
   426             'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
       
   427             'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
       
   428             'include04': ('a{% include "nonexistent" %}b', {}, "ab"),
       
   429 
       
   430             ### NAMED ENDBLOCKS #######################################################
       
   431 
       
   432             # Basic test
       
   433             'namedendblocks01': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock first %}3", {}, '1_2_3'),
       
   434 
       
   435             # Unbalanced blocks
       
   436             'namedendblocks02': ("1{% block first %}_{% block second %}2{% endblock first %}_{% endblock second %}3", {}, template.TemplateSyntaxError),
       
   437             'namedendblocks03': ("1{% block first %}_{% block second %}2{% endblock %}_{% endblock second %}3", {}, template.TemplateSyntaxError),
       
   438             'namedendblocks04': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock third %}3", {}, template.TemplateSyntaxError),
       
   439             'namedendblocks05': ("1{% block first %}_{% block second %}2{% endblock first %}", {}, template.TemplateSyntaxError),
       
   440 
       
   441             # Mixed named and unnamed endblocks
       
   442             'namedendblocks06': ("1{% block first %}_{% block second %}2{% endblock %}_{% endblock first %}3", {}, '1_2_3'),
       
   443             'namedendblocks07': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock %}3", {}, '1_2_3'),
       
   444 
       
   445             ### INHERITANCE ###########################################################
       
   446 
       
   447             # Standard template with no inheritance
       
   448             'inheritance01': ("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}", {}, '1_3_'),
       
   449 
       
   450             # Standard two-level inheritance
       
   451             'inheritance02': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'),
       
   452 
       
   453             # Three-level with no redefinitions on third level
       
   454             'inheritance03': ("{% extends 'inheritance02' %}", {}, '1234'),
       
   455 
       
   456             # Two-level with no redefinitions on second level
       
   457             'inheritance04': ("{% extends 'inheritance01' %}", {}, '1_3_'),
       
   458 
       
   459             # Two-level with double quotes instead of single quotes
       
   460             'inheritance05': ('{% extends "inheritance02" %}', {}, '1234'),
       
   461 
       
   462             # Three-level with variable parent-template name
       
   463             'inheritance06': ("{% extends foo %}", {'foo': 'inheritance02'}, '1234'),
       
   464 
       
   465             # Two-level with one block defined, one block not defined
       
   466             'inheritance07': ("{% extends 'inheritance01' %}{% block second %}5{% endblock %}", {}, '1_35'),
       
   467 
       
   468             # Three-level with one block defined on this level, two blocks defined next level
       
   469             'inheritance08': ("{% extends 'inheritance02' %}{% block second %}5{% endblock %}", {}, '1235'),
       
   470 
       
   471             # Three-level with second and third levels blank
       
   472             'inheritance09': ("{% extends 'inheritance04' %}", {}, '1_3_'),
       
   473 
       
   474             # Three-level with space NOT in a block -- should be ignored
       
   475             'inheritance10': ("{% extends 'inheritance04' %}      ", {}, '1_3_'),
       
   476 
       
   477             # Three-level with both blocks defined on this level, but none on second level
       
   478             'inheritance11': ("{% extends 'inheritance04' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'),
       
   479 
       
   480             # Three-level with this level providing one and second level providing the other
       
   481             'inheritance12': ("{% extends 'inheritance07' %}{% block first %}2{% endblock %}", {}, '1235'),
       
   482 
       
   483             # Three-level with this level overriding second level
       
   484             'inheritance13': ("{% extends 'inheritance02' %}{% block first %}a{% endblock %}{% block second %}b{% endblock %}", {}, '1a3b'),
       
   485 
       
   486             # A block defined only in a child template shouldn't be displayed
       
   487             'inheritance14': ("{% extends 'inheritance01' %}{% block newblock %}NO DISPLAY{% endblock %}", {}, '1_3_'),
       
   488 
       
   489             # A block within another block
       
   490             'inheritance15': ("{% extends 'inheritance01' %}{% block first %}2{% block inner %}inner{% endblock %}{% endblock %}", {}, '12inner3_'),
       
   491 
       
   492             # A block within another block (level 2)
       
   493             'inheritance16': ("{% extends 'inheritance15' %}{% block inner %}out{% endblock %}", {}, '12out3_'),
       
   494 
       
   495             # {% load %} tag (parent -- setup for exception04)
       
   496             'inheritance17': ("{% load testtags %}{% block first %}1234{% endblock %}", {}, '1234'),
       
   497 
       
   498             # {% load %} tag (standard usage, without inheritance)
       
   499             'inheritance18': ("{% load testtags %}{% echo this that theother %}5678", {}, 'this that theother5678'),
       
   500 
       
   501             # {% load %} tag (within a child template)
       
   502             'inheritance19': ("{% extends 'inheritance01' %}{% block first %}{% load testtags %}{% echo 400 %}5678{% endblock %}", {}, '140056783_'),
       
   503 
       
   504             # Two-level inheritance with {{ block.super }}
       
   505             'inheritance20': ("{% extends 'inheritance01' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1_a3_'),
       
   506 
       
   507             # Three-level inheritance with {{ block.super }} from parent
       
   508             'inheritance21': ("{% extends 'inheritance02' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '12a34'),
       
   509 
       
   510             # Three-level inheritance with {{ block.super }} from grandparent
       
   511             'inheritance22': ("{% extends 'inheritance04' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1_a3_'),
       
   512 
       
   513             # Three-level inheritance with {{ block.super }} from parent and grandparent
       
   514             'inheritance23': ("{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", {}, '1_ab3_'),
       
   515 
       
   516             # Inheritance from local context without use of template loader
       
   517             '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'),
       
   518 
       
   519             # Inheritance from local context with variable parent template
       
   520             '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'),
       
   521 
       
   522             ### I18N ##################################################################
       
   523 
       
   524             # {% spaceless %} tag
       
   525             'spaceless01': ("{% spaceless %} <b>    <i> text </i>    </b> {% endspaceless %}", {}, "<b> <i> text </i> </b>"),
       
   526             'spaceless02': ("{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}", {}, "<b> <i> text </i> </b>"),
       
   527             'spaceless03': ("{% spaceless %}<b><i>text</i></b>{% endspaceless %}", {}, "<b><i>text</i></b>"),
       
   528 
       
   529             # simple translation of a string delimited by '
       
   530             'i18n01': ("{% load i18n %}{% trans 'xxxyyyxxx' %}", {}, "xxxyyyxxx"),
       
   531 
       
   532             # simple translation of a string delimited by "
       
   533             'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"),
       
   534 
       
   535             # simple translation of a variable
       
   536             'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': 'xxxyyyxxx'}, "xxxyyyxxx"),
       
   537 
       
   538             # simple translation of a variable and filter
       
   539             'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'XXXYYYXXX'}, "xxxyyyxxx"),
       
   540 
       
   541             # simple translation of a string with interpolation
       
   542             'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"),
       
   543 
       
   544             # simple translation of a string to german
       
   545             'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"),
       
   546 
       
   547             # translation of singular form
       
   548             'i18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}plural{% endblocktrans %}', {'number': 1}, "singular"),
       
   549 
       
   550             # translation of plural form
       
   551             'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}plural{% endblocktrans %}', {'number': 2}, "plural"),
       
   552 
       
   553             # simple non-translation (only marking) of a string to german
       
   554             'i18n09': ('{% load i18n %}{% trans "Page not found" noop %}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
       
   555 
       
   556             # translation of a variable with a translated filter
       
   557             'i18n10': ('{{ bool|yesno:_("ja,nein") }}', {'bool': True}, 'ja'),
       
   558 
       
   559             # translation of a variable with a non-translated filter
       
   560             'i18n11': ('{{ bool|yesno:"ja,nein" }}', {'bool': True}, 'ja'),
       
   561 
       
   562             # usage of the get_available_languages tag
       
   563             'i18n12': ('{% load i18n %}{% get_available_languages as langs %}{% for lang in langs %}{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}', {}, 'de'),
       
   564 
       
   565             # translation of a constant string
       
   566             'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'),
       
   567 
       
   568             ### HANDLING OF TEMPLATE_TAG_IF_INVALID ###################################
       
   569 
       
   570             'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
       
   571             'invalidstr02': ('{{ var|default_if_none:"Foo" }}', {}, ('','INVALID')),
       
   572             'invalidstr03': ('{% for v in var %}({{ v }}){% endfor %}', {}, ''),
       
   573             'invalidstr04': ('{% if var %}Yes{% else %}No{% endif %}', {}, 'No'),
       
   574             'invalidstr04': ('{% if var|default:"Foo" %}Yes{% else %}No{% endif %}', {}, 'Yes'),
       
   575 
       
   576             ### MULTILINE #############################################################
       
   577 
       
   578             'multiline01': ("""
       
   579                             Hello,
       
   580                             boys.
       
   581                             How
       
   582                             are
       
   583                             you
       
   584                             gentlemen.
       
   585                             """,
       
   586                             {},
       
   587                             """
       
   588                             Hello,
       
   589                             boys.
       
   590                             How
       
   591                             are
       
   592                             you
       
   593                             gentlemen.
       
   594                             """),
       
   595 
       
   596             ### REGROUP TAG ###########################################################
       
   597             'regroup01': ('{% regroup data by bar as grouped %}' + \
       
   598                           '{% for group in grouped %}' + \
       
   599                           '{{ group.grouper }}:' + \
       
   600                           '{% for item in group.list %}' + \
       
   601                           '{{ item.foo }}' + \
       
   602                           '{% endfor %},' + \
       
   603                           '{% endfor %}',
       
   604                           {'data': [ {'foo':'c', 'bar':1},
       
   605                                      {'foo':'d', 'bar':1},
       
   606                                      {'foo':'a', 'bar':2},
       
   607                                      {'foo':'b', 'bar':2},
       
   608                                      {'foo':'x', 'bar':3}  ]},
       
   609                           '1:cd,2:ab,3:x,'),
       
   610 
       
   611             # Test for silent failure when target variable isn't found
       
   612             'regroup02': ('{% regroup data by bar as grouped %}' + \
       
   613                           '{% for group in grouped %}' + \
       
   614                           '{{ group.grouper }}:' + \
       
   615                           '{% for item in group.list %}' + \
       
   616                           '{{ item.foo }}' + \
       
   617                           '{% endfor %},' + \
       
   618                           '{% endfor %}',
       
   619                           {}, ''),
       
   620 
       
   621             ### TEMPLATETAG TAG #######################################################
       
   622             'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
       
   623             'templatetag02': ('{% templatetag closeblock %}', {}, '%}'),
       
   624             'templatetag03': ('{% templatetag openvariable %}', {}, '{{'),
       
   625             'templatetag04': ('{% templatetag closevariable %}', {}, '}}'),
       
   626             'templatetag05': ('{% templatetag %}', {}, template.TemplateSyntaxError),
       
   627             'templatetag06': ('{% templatetag foo %}', {}, template.TemplateSyntaxError),
       
   628             'templatetag07': ('{% templatetag openbrace %}', {}, '{'),
       
   629             'templatetag08': ('{% templatetag closebrace %}', {}, '}'),
       
   630             'templatetag09': ('{% templatetag openbrace %}{% templatetag openbrace %}', {}, '{{'),
       
   631             'templatetag10': ('{% templatetag closebrace %}{% templatetag closebrace %}', {}, '}}'),
       
   632             'templatetag11': ('{% templatetag opencomment %}', {}, '{#'),
       
   633             'templatetag12': ('{% templatetag closecomment %}', {}, '#}'),
       
   634 
       
   635             ### WIDTHRATIO TAG ########################################################
       
   636             'widthratio01': ('{% widthratio a b 0 %}', {'a':50,'b':100}, '0'),
       
   637             'widthratio02': ('{% widthratio a b 100 %}', {'a':0,'b':0}, ''),
       
   638             'widthratio03': ('{% widthratio a b 100 %}', {'a':0,'b':100}, '0'),
       
   639             'widthratio04': ('{% widthratio a b 100 %}', {'a':50,'b':100}, '50'),
       
   640             'widthratio05': ('{% widthratio a b 100 %}', {'a':100,'b':100}, '100'),
       
   641 
       
   642             # 62.5 should round to 63
       
   643             'widthratio06': ('{% widthratio a b 100 %}', {'a':50,'b':80}, '63'),
       
   644 
       
   645             # 71.4 should round to 71
       
   646             'widthratio07': ('{% widthratio a b 100 %}', {'a':50,'b':70}, '71'),
       
   647 
       
   648             # Raise exception if we don't have 3 args, last one an integer
       
   649             'widthratio08': ('{% widthratio %}', {}, template.TemplateSyntaxError),
       
   650             'widthratio09': ('{% widthratio a b %}', {'a':50,'b':100}, template.TemplateSyntaxError),
       
   651             'widthratio10': ('{% widthratio a b 100.0 %}', {'a':50,'b':100}, template.TemplateSyntaxError),
       
   652 
       
   653             ### NOW TAG ########################################################
       
   654             # Simple case
       
   655             'now01' : ('{% now "j n Y"%}', {}, str(datetime.now().day) + ' ' + str(datetime.now().month) + ' ' + str(datetime.now().year)),
       
   656 
       
   657             # Check parsing of escaped and special characters
       
   658             'now02' : ('{% now "j "n" Y"%}', {}, template.TemplateSyntaxError),
       
   659         #    'now03' : ('{% now "j \"n\" Y"%}', {}, str(datetime.now().day) + '"' + str(datetime.now().month) + '"' + str(datetime.now().year)),
       
   660         #    'now04' : ('{% now "j \nn\n Y"%}', {}, str(datetime.now().day) + '\n' + str(datetime.now().month) + '\n' + str(datetime.now().year))
       
   661 
       
   662             ### TIMESINCE TAG ##################################################
       
   663             # Default compare with datetime.now()
       
   664             'timesince01' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
       
   665             'timesince02' : ('{{ a|timesince }}', {'a':(datetime.now() - timedelta(days=1, minutes = 1))}, '1 day'),
       
   666             'timesince03' : ('{{ a|timesince }}', {'a':(datetime.now() -
       
   667                 timedelta(hours=1, minutes=25, seconds = 10))}, '1 hour, 25 minutes'),
       
   668 
       
   669             # Compare to a given parameter
       
   670             'timesince04' : ('{{ a|timesince:b }}', {'a':NOW + timedelta(days=2), 'b':NOW + timedelta(days=1)}, '1 day'),
       
   671             'timesince05' : ('{{ a|timesince:b }}', {'a':NOW + timedelta(days=2, minutes=1), 'b':NOW + timedelta(days=2)}, '1 minute'),
       
   672 
       
   673             # Check that timezone is respected
       
   674             'timesince06' : ('{{ a|timesince:b }}', {'a':NOW_tz + timedelta(hours=8), 'b':NOW_tz}, '8 hours'),
       
   675 
       
   676             ### TIMEUNTIL TAG ##################################################
       
   677             # Default compare with datetime.now()
       
   678             'timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
       
   679             'timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
       
   680             'timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8 hours, 10 minutes'),
       
   681 
       
   682             # Compare to a given parameter
       
   683             'timeuntil04' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=1), 'b':NOW - timedelta(days=2)}, '1 day'),
       
   684             'timeuntil05' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=2), 'b':NOW - timedelta(days=2, minutes=1)}, '1 minute'),
       
   685 
       
   686             ### URL TAG ########################################################
       
   687             # Successes
       
   688             'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
       
   689             'url02' : ('{% url regressiontests.templates.views.client_action client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
       
   690             'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
       
   691 
       
   692             # Failures
       
   693             'url04' : ('{% url %}', {}, template.TemplateSyntaxError),
       
   694             'url05' : ('{% url no_such_view %}', {}, ''),
       
   695             'url06' : ('{% url regressiontests.templates.views.client no_such_param="value" %}', {}, ''),
       
   696         }
       
   697 
       
   698         # Register our custom template loader.
       
   699         def test_template_loader(template_name, template_dirs=None):
       
   700             "A custom template loader that loads the unit-test templates."
       
   701             try:
       
   702                 return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)
       
   703             except KeyError:
       
   704                 raise template.TemplateDoesNotExist, template_name
       
   705 
       
   706         old_template_loaders = loader.template_source_loaders
       
   707         loader.template_source_loaders = [test_template_loader]
       
   708 
       
   709         failures = []
       
   710         tests = TEMPLATE_TESTS.items()
       
   711         tests.sort()
       
   712 
       
   713         # Turn TEMPLATE_DEBUG off, because tests assume that.
       
   714         old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
       
   715 
       
   716         # Set TEMPLATE_STRING_IF_INVALID to a known string
       
   717         old_invalid = settings.TEMPLATE_STRING_IF_INVALID
       
   718 
       
   719         for name, vals in tests:
       
   720             install()
       
   721 
       
   722             if isinstance(vals[2], tuple):
       
   723                 normal_string_result = vals[2][0]
       
   724                 invalid_string_result = vals[2][1]
       
   725             else:
       
   726                 normal_string_result = vals[2]
       
   727                 invalid_string_result = vals[2]
       
   728 
       
   729             if 'LANGUAGE_CODE' in vals[1]:
       
   730                 activate(vals[1]['LANGUAGE_CODE'])
       
   731             else:
       
   732                 activate('en-us')
       
   733 
       
   734             for invalid_str, result in [('', normal_string_result),
       
   735                                         ('INVALID', invalid_string_result)]:
       
   736                 settings.TEMPLATE_STRING_IF_INVALID = invalid_str
       
   737                 try:
       
   738                     output = loader.get_template(name).render(template.Context(vals[1]))
       
   739                 except Exception, e:
       
   740                     if e.__class__ != result:
       
   741                         failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s" % (invalid_str, name, e.__class__, e))
       
   742                     continue
       
   743                 if output != result:
       
   744                     failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
       
   745 
       
   746             if 'LANGUAGE_CODE' in vals[1]:
       
   747                 deactivate()
       
   748 
       
   749         loader.template_source_loaders = old_template_loaders
       
   750         deactivate()
       
   751         settings.TEMPLATE_DEBUG = old_td
       
   752         settings.TEMPLATE_STRING_IF_INVALID = old_invalid
       
   753 
       
   754         self.assertEqual(failures, [], '\n'.join(failures))
       
   755 
       
   756 if __name__ == "__main__":
       
   757     unittest.main()