Started working on unittest powered testsuite.
authorArmin Ronacher <armin.ronacher@active-4.com>
Tue, 9 Feb 2010 15:05:08 +0000 (16:05 +0100)
committerArmin Ronacher <armin.ronacher@active-4.com>
Tue, 9 Feb 2010 15:05:08 +0000 (16:05 +0100)
--HG--
branch : trunk

jinja2/compiler.py
jinja2/testsuite/__init__.py [new file with mode: 0644]
jinja2/testsuite/ext.py [new file with mode: 0644]
jinja2/testsuite/filters.py [new file with mode: 0644]
jinja2/testsuite/res/__init__.py [new file with mode: 0644]
jinja2/testsuite/res/templates/broken.html [new file with mode: 0644]
jinja2/testsuite/res/templates/foo/test.html [new file with mode: 0644]
jinja2/testsuite/res/templates/syntaxerror.html [new file with mode: 0644]
jinja2/testsuite/res/templates/test.html [new file with mode: 0644]
setup.py

index 59d06c7400625cd13639febb0e3ae58ae5942bb2..672d696e270f19551219366a51c6bcce19e9eb25 100644 (file)
@@ -6,7 +6,7 @@
     Compiles nodes into python code.
 
     :copyright: (c) 2010 by the Jinja Team.
-    :license: BSD.
+    :license: BSD, see LICENSE for more details.
 """
 from cStringIO import StringIO
 from itertools import chain
diff --git a/jinja2/testsuite/__init__.py b/jinja2/testsuite/__init__.py
new file mode 100644 (file)
index 0000000..c0327c1
--- /dev/null
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.testsuite
+    ~~~~~~~~~~~~~~~~
+
+    All the unittests of Jinja2.  These tests can be executed by
+    either running run-tests.py using multiple Python versions at
+    the same time.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import unittest
+from jinja2 import loaders
+
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+dict_loader = loaders.DictLoader({
+    'justdict.html':        'FOO'
+})
+package_loader = loaders.PackageLoader('jinja2.testsuite.res', 'templates')
+filesystem_loader = loaders.FileSystemLoader(here + 'res/templates')
+function_loader = loaders.FunctionLoader({'justfunction.html': 'FOO'}.get)
+choice_loader = loaders.ChoiceLoader([dict_loader, package_loader])
+prefix_loader = loaders.PrefixLoader({
+    'a':        filesystem_loader,
+    'b':        dict_loader
+})
+
+
+class JinjaTestCase(unittest.TestCase):
+
+    ### use only these methods for testing.  If you need standard
+    ### unittest method, wrap them!
+
+    def assert_equal(self, a, b):
+        return self.assertEqual(a, b)
+
+    def assert_raises(self, *args, **kwargs):
+        return self.assertRaises(*args, **kwargs)
+
+
+def suite():
+    from jinja2.testsuite import ext, filters
+    suite = unittest.TestSuite()
+    suite.addTest(ext.suite())
+    suite.addTest(filters.suite())
+    return suite
diff --git a/jinja2/testsuite/ext.py b/jinja2/testsuite/ext.py
new file mode 100644 (file)
index 0000000..57298ac
--- /dev/null
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.testsuite.ext
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Tests for the extensions.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import unittest
+
+from jinja2.testsuite import JinjaTestCase, filesystem_loader
+
+from jinja2 import Environment, nodes
+from jinja2.ext import Extension
+from jinja2.lexer import Token, count_newlines
+
+
+importable_object = 23
+
+_gettext_re = re.compile(r'_\((.*?)\)(?s)')
+
+
+class TestExtension(Extension):
+    tags = set(['test'])
+    ext_attr = 42
+
+    def parse(self, parser):
+        return nodes.Output([self.call_method('_dump', [
+            nodes.EnvironmentAttribute('sandboxed'),
+            self.attr('ext_attr'),
+            nodes.ImportedName(__name__ + '.importable_object'),
+            nodes.ContextReference()
+        ])]).set_lineno(parser.stream.next().lineno)
+
+    def _dump(self, sandboxed, ext_attr, imported_object, context):
+        return '%s|%s|%s|%s' % (
+            sandboxed,
+            ext_attr,
+            imported_object,
+            context.blocks
+        )
+
+
+class PreprocessorExtension(Extension):
+
+    def preprocess(self, source, name, filename=None):
+        return source.replace('[[TEST]]', '({{ foo }})')
+
+
+class StreamFilterExtension(Extension):
+
+    def filter_stream(self, stream):
+        for token in stream:
+            if token.type == 'data':
+                for t in self.interpolate(token):
+                    yield t
+            else:
+                yield token
+
+    def interpolate(self, token):
+        pos = 0
+        end = len(token.value)
+        lineno = token.lineno
+        while 1:
+            match = _gettext_re.search(token.value, pos)
+            if match is None:
+                break
+            value = token.value[pos:match.start()]
+            if value:
+                yield Token(lineno, 'data', value)
+            lineno += count_newlines(token.value)
+            yield Token(lineno, 'variable_begin', None)
+            yield Token(lineno, 'name', 'gettext')
+            yield Token(lineno, 'lparen', None)
+            yield Token(lineno, 'string', match.group(1))
+            yield Token(lineno, 'rparen', None)
+            yield Token(lineno, 'variable_end', None)
+            pos = match.end()
+        if pos < end:
+            yield Token(lineno, 'data', token.value[pos:])
+
+
+class ExtensionsTestCase(JinjaTestCase):
+
+    def test_loop_controls(self):
+        env = Environment(extensions=['jinja2.ext.loopcontrols'])
+
+        tmpl = env.from_string('''
+            {%- for item in [1, 2, 3, 4] %}
+                {%- if item % 2 == 0 %}{% continue %}{% endif -%}
+                {{ item }}
+            {%- endfor %}''')
+        assert tmpl.render() == '13'
+
+        tmpl = env.from_string('''
+            {%- for item in [1, 2, 3, 4] %}
+                {%- if item > 2 %}{% break %}{% endif -%}
+                {{ item }}
+            {%- endfor %}''')
+        assert tmpl.render() == '12'
+
+    def test_do(self):
+        env = Environment(extensions=['jinja2.ext.do'])
+        tmpl = env.from_string('''
+            {%- set items = [] %}
+            {%- for char in "foo" %}
+                {%- do items.append(loop.index0 ~ char) %}
+            {%- endfor %}{{ items|join(', ') }}''')
+        assert tmpl.render() == '0f, 1o, 2o'
+
+    def test_with(self):
+        env = Environment(extensions=['jinja2.ext.with_'])
+        tmpl = env.from_string('''\
+        {% with a=42, b=23 -%}
+            {{ a }} = {{ b }}
+        {% endwith -%}
+            {{ a }} = {{ b }}\
+        ''')
+        assert [x.strip() for x in tmpl.render(a=1, b=2).splitlines()] \
+            == ['42 = 23', '1 = 2']
+
+    def test_extension_nodes(self):
+        env = Environment(extensions=[TestExtension])
+        tmpl = env.from_string('{% test %}')
+        assert tmpl.render() == 'False|42|23|{}'
+
+    def test_identifier(self):
+        assert TestExtension.identifier == __name__ + '.TestExtension'
+
+    def test_rebinding(self):
+        original = Environment(extensions=[TestExtension])
+        overlay = original.overlay()
+        for env in original, overlay:
+            for ext in env.extensions.itervalues():
+                assert ext.environment is env
+
+    def test_preprocessor_extension(self):
+        env = Environment(extensions=[PreprocessorExtension])
+        tmpl = env.from_string('{[[TEST]]}')
+        assert tmpl.render(foo=42) == '{(42)}'
+
+    def test_streamfilter_extension(self):
+        env = Environment(extensions=[StreamFilterExtension])
+        env.globals['gettext'] = lambda x: x.upper()
+        tmpl = env.from_string('Foo _(bar) Baz')
+        out = tmpl.render()
+        assert out == 'Foo BAR Baz'
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(ExtensionsTestCase))
+    return suite
diff --git a/jinja2/testsuite/filters.py b/jinja2/testsuite/filters.py
new file mode 100644 (file)
index 0000000..e3bca02
--- /dev/null
@@ -0,0 +1,341 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.testsuite.filters
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Tests for the jinja filters.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import unittest
+from jinja2.testsuite import JinjaTestCase
+
+from jinja2 import Markup, Environment
+
+env = Environment()
+
+
+CAPITALIZE = '''{{ "foo bar"|capitalize }}'''
+CENTER = '''{{ "foo"|center(9) }}'''
+DEFAULT = '''{{ missing|default("no") }}|{{ false|default('no') }}|\
+{{ false|default('no', true) }}|{{ given|default("no") }}'''
+DICTSORT = '''{{ foo|dictsort }}|\
+{{ foo|dictsort(true) }}|\
+{{ foo|dictsort(false, 'value') }}'''
+BATCH = '''{{ foo|batch(3)|list }}|{{ foo|batch(3, 'X')|list }}'''
+SLICE = '''{{ foo|slice(3)|list }}|{{ foo|slice(3, 'X')|list }}'''
+ESCAPE = '''{{ '<">&'|escape }}'''
+STRIPTAGS = '''{{ foo|striptags }}'''
+FILESIZEFORMAT = '{{ 100|filesizeformat }}|\
+{{ 1000|filesizeformat }}|\
+{{ 1000000|filesizeformat }}|\
+{{ 1000000000|filesizeformat }}|\
+{{ 1000000000000|filesizeformat }}|\
+{{ 100|filesizeformat(true) }}|\
+{{ 1000|filesizeformat(true) }}|\
+{{ 1000000|filesizeformat(true) }}|\
+{{ 1000000000|filesizeformat(true) }}|\
+{{ 1000000000000|filesizeformat(true) }}'
+FIRST = '''{{ foo|first }}'''
+FLOAT = '''{{ "42"|float }}|{{ "ajsghasjgd"|float }}|{{ "32.32"|float }}'''
+FORMAT = '''{{ "%s|%s"|format("a", "b") }}'''
+INDENT = '''{{ foo|indent(2) }}|{{ foo|indent(2, true) }}'''
+INT = '''{{ "42"|int }}|{{ "ajsghasjgd"|int }}|{{ "32.32"|int }}'''
+JOIN = '''{{ [1, 2, 3]|join("|") }}'''
+LAST = '''{{ foo|last }}'''
+LENGTH = '''{{ "hello world"|length }}'''
+LOWER = '''{{ "FOO"|lower }}'''
+PPRINT = '''{{ data|pprint }}'''
+RANDOM = '''{{ seq|random }}'''
+REVERSE = '''{{ "foobar"|reverse|join }}|{{ [1, 2, 3]|reverse|list }}'''
+STRING = '''{{ range(10)|string }}'''
+TITLE = '''{{ "foo bar"|title }}'''
+TRIM = '''{{ "      foo       "|trim }}'''
+TRUNCATE = '''{{ data|truncate(15, true, ">>>") }}|\
+{{ data|truncate(15, false, ">>>") }}|\
+{{ smalldata|truncate(15) }}'''
+UPPER = '''{{ "foo"|upper }}'''
+URLIZE = '''{{ "foo http://www.example.com/ bar"|urlize }}'''
+WORDCOUNT = '''{{ "foo bar baz"|wordcount }}'''
+BLOCK = '''{% filter lower|escape %}<HEHE>{% endfilter %}'''
+CHAINING = '''{{ ['<foo>', '<bar>']|first|upper|escape }}'''
+SUM = '''{{ [1, 2, 3, 4, 5, 6]|sum }}'''
+ABS = '''{{ -1|abs }}|{{ 1|abs }}'''
+ROUND = '''{{ 2.7|round }}|{{ 2.1|round }}|\
+{{ 2.1234|round(2, 'floor') }}|{{ 2.1|round(0, 'ceil') }}'''
+XMLATTR = '''{{ {'foo': 42, 'bar': 23, 'fish': none,
+'spam': missing, 'blub:blub': '<?>'}|xmlattr }}'''
+SORT1 = '''{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}'''
+GROUPBY = '''{% for grouper, list in [{'foo': 1, 'bar': 2},
+                 {'foo': 2, 'bar': 3},
+                 {'foo': 1, 'bar': 1},
+                 {'foo': 3, 'bar': 4}]|groupby('foo') -%}
+{{ grouper }}: {{ list|join(', ') }}
+{% endfor %}'''
+FILTERTAG = '''{% filter upper|replace('FOO', 'foo') %}foobar{% endfilter %}'''
+SORT2 = '''{{ ['foo', 'Bar', 'blah']|sort }}'''
+
+
+class FilterTestCase(JinjaTestCase):
+
+    def test_capitalize(self):
+        tmpl = env.from_string('{{ "foo bar"|capitalize }}')
+        assert tmpl.render() == 'Foo bar'
+
+    def test_center(self):
+        tmpl = env.from_string('{{ "foo"|center(9) }}')
+        assert tmpl.render() == '   foo   '
+
+    def test_default(self):
+        tmpl = env.from_string(
+            "{{ missing|default('no') }}|{{ false|default('no') }}|"
+            "{{ false|default('no', true) }}|{{ given|default('no') }}"
+        )
+        assert tmpl.render(given='yes') == 'no|False|no|yes'
+
+    def test_dictsort(self):
+        tmpl = env.from_string(
+            '{{ foo|dictsort }}|'
+            '{{ foo|dictsort(true) }}|'
+            '{{ foo|dictsort(false, "value") }}'
+        )
+        out = tmpl.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3})
+        assert out == ("[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]|"
+                       "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]|"
+                       "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]")
+
+    def test_batch(self):
+        tmpl = env.from_string("{{ foo|batch(3)|list }}|"
+                               "{{ foo|batch(3, 'X')|list }}")
+        out = tmpl.render(foo=range(10))
+        assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|"
+                       "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]")
+
+    def test_slice(self):
+        tmpl = env.from_string('{{ foo|slice(3)|list }}|'
+                               '{{ foo|slice(3, "X")|list }}')
+        out = tmpl.render(foo=range(10))
+        assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
+                       "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]")
+
+    def test_escape(self):
+        tmpl = env.from_string('''{{ '<">&'|escape }}''')
+        out = tmpl.render()
+        assert out == '&lt;&#34;&gt;&amp;'
+
+    def test_striptags(self):
+        tmpl = env.from_string('''{{ foo|striptags }}''')
+        out = tmpl.render(foo='  <p>just a small   \n <a href="#">'
+                          'example</a> link</p>\n<p>to a webpage</p> '
+                          '<!-- <p>and some commented stuff</p> -->')
+        assert out == 'just a small example link to a webpage'
+
+    def test_filesizeformat(self):
+        tmpl = env.from_string(
+            '{{ 100|filesizeformat }}|'
+            '{{ 1000|filesizeformat }}|'
+            '{{ 1000000|filesizeformat }}|'
+            '{{ 1000000000|filesizeformat }}|'
+            '{{ 1000000000000|filesizeformat }}|'
+            '{{ 100|filesizeformat(true) }}|'
+            '{{ 1000|filesizeformat(true) }}|'
+            '{{ 1000000|filesizeformat(true) }}|'
+            '{{ 1000000000|filesizeformat(true) }}|'
+            '{{ 1000000000000|filesizeformat(true) }}'
+        )
+        out = tmpl.render()
+        assert out == (
+            '100 Bytes|1.0 KB|1.0 MB|1.0 GB|1000.0 GB|'
+            '100 Bytes|1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB'
+        )
+
+    def test_first(self):
+        tmpl = env.from_string('{{ foo|first }}')
+        out = tmpl.render(foo=range(10))
+        assert out == '0'
+
+    def test_float(self):
+        tmpl = env.from_string('{{ "42"|float }}|'
+                               '{{ "ajsghasjgd"|float }}|'
+                               '{{ "32.32"|float }}')
+        out = tmpl.render()
+        assert out == '42.0|0.0|32.32'
+
+    def test_format(self):
+        tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''')
+        out = tmpl.render()
+        assert out == 'a|b'
+
+    def test_indent(self):
+        tmpl = env.from_string('{{ foo|indent(2) }}|{{ foo|indent(2, true) }}')
+        text = '\n'.join([' '.join(['foo', 'bar'] * 2)] * 2)
+        out = tmpl.render(foo=text)
+        assert out == ('foo bar foo bar\n  foo bar foo bar|  '
+                       'foo bar foo bar\n  foo bar foo bar')
+
+    def test_int(self):
+        tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|'
+                               '{{ "32.32"|int }}')
+        out = tmpl.render()
+        assert out == '42|0|32'
+
+    def test_join(self):
+        tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}')
+        out = tmpl.render()
+        assert out == '1|2|3'
+
+        env2 = Environment(autoescape=True)
+        tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
+        assert tmpl.render() == '&lt;foo&gt;<span>foo</span>'
+
+    def test_last(self):
+        tmpl = env.from_string('''{{ foo|last }}''')
+        out = tmpl.render(foo=range(10))
+        assert out == '9'
+
+    def test_length(self):
+        tmpl = env.from_string('''{{ "hello world"|length }}''')
+        out = tmpl.render()
+        assert out == '11'
+
+    def test_lower(self):
+        tmpl = env.from_string('''{{ "FOO"|lower }}''')
+        out = tmpl.render()
+        assert out == 'foo'
+
+    def test_pprint(self):
+        from pprint import pformat
+        tmpl = env.from_string('''{{ data|pprint }}''')
+        data = range(1000)
+        assert tmpl.render(data=data) == pformat(data)
+
+    def test_random(self):
+        tmpl = env.from_string('''{{ seq|random }}''')
+        seq = range(100)
+        for _ in range(10):
+            assert int(tmpl.render(seq=seq)) in seq
+
+    def test_reverse(self):
+        tmpl = env.from_string('{{ "foobar"|reverse|join }}|'
+                               '{{ [1, 2, 3]|reverse|list }}')
+        assert tmpl.render() == 'raboof|[3, 2, 1]'
+
+    def test_string(self):
+        tmpl = env.from_string('''{{ range(10)|string }}''')
+        assert tmpl.render(foo=range(10)) == unicode(xrange(10))
+
+    def test_title(self):
+        tmpl = env.from_string('''{{ "foo bar"|title }}''')
+        assert tmpl.render() == "Foo Bar"
+
+    def test_truncate(self):
+        tmpl = env.from_string(
+            '{{ data|truncate(15, true, ">>>") }}|'
+            '{{ data|truncate(15, false, ">>>") }}|'
+            '{{ smalldata|truncate(15) }}'
+        )
+        out = tmpl.render(data='foobar baz bar' * 1000,
+                          smalldata='foobar baz bar')
+        assert out == 'foobar baz barf>>>|foobar baz >>>|foobar baz bar'
+
+    def test_upper(self):
+        tmpl = env.from_string('{{ "foo"|upper }}')
+        assert tmpl.render() == 'FOO'
+
+    def test_urlize(self):
+        tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
+        assert tmpl.render() == 'foo <a href="http://www.example.com/">'\
+                                'http://www.example.com/</a> bar'
+
+    def test_wordcount(self):
+        tmpl = env.from_string('{{ "foo bar baz"|wordcount }}')
+        assert tmpl.render() == '3'
+
+    def test_block(self):
+        tmpl = env.from_string('{% filter lower|escape %}<HEHE>{% endfilter %}')
+        assert tmpl.render() == '&lt;hehe&gt;'
+
+    def test_chaining(self):
+        tmpl = env.from_string('''{{ ['<foo>', '<bar>']|first|upper|escape }}''')
+        assert tmpl.render() == '&lt;FOO&gt;'
+
+    def test_sum(self):
+        tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''')
+        assert tmpl.render() == '21'
+
+    def test_abs(self):
+        tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''')
+        return tmpl.render() == '1|1'
+
+    def test_round(self):
+        tmpl = env.from_string('{{ 2.7|round }}|{{ 2.1|round }}|'
+                               "{{ 2.1234|round(2, 'floor') }}|"
+                               "{{ 2.1|round(0, 'ceil') }}")
+        return tmpl.render() == '3.0|2.0|2.1|3.0'
+
+    def test_xmlattr(self):
+        tmpl = env.from_string("{{ {'foo': 42, 'bar': 23, 'fish': none, "
+                               "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}")
+        out = tmpl.render().split()
+        assert len(out) == 3
+        assert 'foo="42"' in out
+        assert 'bar="23"' in out
+        assert 'blub:blub="&lt;?&gt;"' in out
+
+    def test_sort1(self):
+        tmpl = env.from_string('{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}')
+        assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+
+    def test_groupby(self):
+        tmpl = env.from_string('''
+        {%- for grouper, list in [{'foo': 1, 'bar': 2},
+                                  {'foo': 2, 'bar': 3},
+                                  {'foo': 1, 'bar': 1},
+                                  {'foo': 3, 'bar': 4}]|groupby('foo') -%}
+            {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}|
+        {%- endfor %}''')
+        assert tmpl.render().split('|') == [
+            "1: 1, 2: 1, 1",
+            "2: 2, 3",
+            "3: 3, 4",
+            ""
+        ]
+
+    def test_filtertag(self):
+        tmpl = env.from_string("{% filter upper|replace('FOO', 'foo') %}"
+                               "foobar{% endfilter %}")
+        assert tmpl.render() == 'fooBAR'
+
+    def test_replace(self):
+        env = Environment()
+        tmpl = env.from_string('{{ string|replace("o", 42) }}')
+        assert tmpl.render(string='<foo>') == '<f4242>'
+        env = Environment(autoescape=True)
+        tmpl = env.from_string('{{ string|replace("o", 42) }}')
+        assert tmpl.render(string='<foo>') == '&lt;f4242&gt;'
+        tmpl = env.from_string('{{ string|replace("<", 42) }}')
+        assert tmpl.render(string='<foo>') == '42foo&gt;'
+        tmpl = env.from_string('{{ string|replace("o", ">x<") }}')
+        assert tmpl.render(string=Markup('foo')) == 'f&gt;x&lt;&gt;x&lt;'
+
+    def test_forceescape(self):
+        tmpl = env.from_string('{{ x|forceescape }}')
+        assert tmpl.render(x=Markup('<div />')) == u'&lt;div /&gt;'
+
+    def test_safe(self):
+        env = Environment(autoescape=True)
+        tmpl = env.from_string('{{ "<div>foo</div>"|safe }}')
+        assert tmpl.render() == '<div>foo</div>'
+        tmpl = env.from_string('{{ "<div>foo</div>" }}')
+        assert tmpl.render() == '&lt;div&gt;foo&lt;/div&gt;'
+
+    def test_sort2(self):
+        tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''')
+        assert tmpl.render() == "['Bar', 'blah', 'foo']"
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(FilterTestCase))
+    return suite
diff --git a/jinja2/testsuite/res/__init__.py b/jinja2/testsuite/res/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/jinja2/testsuite/res/templates/broken.html b/jinja2/testsuite/res/templates/broken.html
new file mode 100644 (file)
index 0000000..77669fa
--- /dev/null
@@ -0,0 +1,3 @@
+Before
+{{ fail() }}
+After
diff --git a/jinja2/testsuite/res/templates/foo/test.html b/jinja2/testsuite/res/templates/foo/test.html
new file mode 100644 (file)
index 0000000..b7d6715
--- /dev/null
@@ -0,0 +1 @@
+FOO
diff --git a/jinja2/testsuite/res/templates/syntaxerror.html b/jinja2/testsuite/res/templates/syntaxerror.html
new file mode 100644 (file)
index 0000000..f21b817
--- /dev/null
@@ -0,0 +1,4 @@
+Foo
+{% for item in broken %}
+  ...
+{% endif %}
diff --git a/jinja2/testsuite/res/templates/test.html b/jinja2/testsuite/res/templates/test.html
new file mode 100644 (file)
index 0000000..ba578e4
--- /dev/null
@@ -0,0 +1 @@
+BAR
index 134cc97168c9c66ead5307193f6abaa3526151a8..f69fecc7c01370ab0329548b589b4687954e022f 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -76,7 +76,7 @@ setup(
         'Topic :: Software Development :: Libraries :: Python Modules',
         'Topic :: Text Processing :: Markup :: HTML'
     ],
-    packages=['jinja2'],
+    packages=['jinja2', 'jinja2.testsuite'],
     features={
         'speedups': Feature("optional C speed-enhancements",
             standard=False,
@@ -86,6 +86,7 @@ setup(
         )
     },
     extras_require={'i18n': ['Babel>=0.8']},
+    test_suite='jinja2.testsuite.suite',
     entry_points="""
     [babel.extractors]
     jinja2 = jinja2.ext:babel_extract[i18n]