cmlenz@1: # -*- coding: utf-8 -*-
cmlenz@1: #
cmlenz@66: # Copyright (C) 2006 Edgewall Software
cmlenz@1: # All rights reserved.
cmlenz@1: #
cmlenz@1: # This software is licensed as described in the file COPYING, which
cmlenz@1: # you should have received as part of this distribution. The terms
cmlenz@66: # are also available at http://markup.edgewall.org/wiki/License.
cmlenz@1: #
cmlenz@1: # This software consists of voluntary contributions made by many
cmlenz@1: # individuals. For the exact contribution history, see the revision
cmlenz@66: # history and logs, available at http://markup.edgewall.org/log/.
cmlenz@1:
cmlenz@1: import doctest
cmlenz@1: import unittest
cmlenz@1: import sys
cmlenz@1:
cmlenz@54: from markup.core import Markup, Stream
cmlenz@1: from markup.template import BadDirectiveError, Context, Template, \
cmlenz@1: TemplateSyntaxError
cmlenz@1:
cmlenz@1:
cmlenz@50: class AttrsDirectiveTestCase(unittest.TestCase):
cmlenz@50: """Tests for the `py:attrs` template directive."""
cmlenz@50:
cmlenz@50: def test_combined_with_loop(self):
cmlenz@50: """
cmlenz@50: Verify that the directive has access to the loop variables.
cmlenz@50: """
cmlenz@61: tmpl = Template("""
cmlenz@50:
cmlenz@50: """)
cmlenz@50: items = [{'id': 1, 'class': 'foo'}, {'id': 2, 'class': 'bar'}]
cmlenz@50: self.assertEqual("""
cmlenz@50:
cmlenz@50: """, str(tmpl.generate(Context(items=items))))
cmlenz@50:
cmlenz@54: def test_update_existing_attr(self):
cmlenz@54: """
cmlenz@54: Verify that an attribute value that evaluates to `None` removes an
cmlenz@54: existing attribute of that name.
cmlenz@54: """
cmlenz@61: tmpl = Template("""
cmlenz@54:
cmlenz@54: """)
cmlenz@54: self.assertEqual("""
cmlenz@54:
cmlenz@54: """, str(tmpl.generate()))
cmlenz@54:
cmlenz@54: def test_remove_existing_attr(self):
cmlenz@54: """
cmlenz@54: Verify that an attribute value that evaluates to `None` removes an
cmlenz@54: existing attribute of that name.
cmlenz@54: """
cmlenz@61: tmpl = Template("""
cmlenz@54:
cmlenz@54: """)
cmlenz@54: self.assertEqual("""
cmlenz@54:
cmlenz@54: """, str(tmpl.generate()))
cmlenz@54:
cmlenz@50:
cmlenz@53: class ChooseDirectiveTestCase(unittest.TestCase):
cmlenz@53: """Tests for the `py:choose` template directive and the complementary
cmlenz@53: directives `py:when` and `py:otherwise`."""
cmlenz@53:
cmlenz@53: def test_multiple_true_whens(self):
cmlenz@53: """
cmlenz@53: Verify that, if multiple `py:when` bodies match, only the first is
cmlenz@53: output.
cmlenz@53: """
cmlenz@61: tmpl = Template("""
cmlenz@53: 1
cmlenz@53: 2
cmlenz@53: 3
cmlenz@53:
""")
cmlenz@53: self.assertEqual("""
cmlenz@53: 1
cmlenz@53:
""", str(tmpl.generate()))
cmlenz@53:
cmlenz@53: def test_otherwise(self):
cmlenz@61: tmpl = Template("""
cmlenz@53: hidden
cmlenz@53: hello
cmlenz@53:
""")
cmlenz@53: self.assertEqual("""
cmlenz@53: hello
cmlenz@53:
""", str(tmpl.generate()))
cmlenz@53:
cmlenz@53: def test_nesting(self):
cmlenz@53: """
cmlenz@53: Verify that `py:choose` blocks can be nested:
cmlenz@53: """
cmlenz@61: tmpl = Template("""
cmlenz@53:
cmlenz@53:
cmlenz@53: 2
cmlenz@53: 3
cmlenz@53:
cmlenz@53:
cmlenz@53: """)
cmlenz@53: self.assertEqual("""
cmlenz@53:
cmlenz@53:
cmlenz@53: 3
cmlenz@53:
cmlenz@53:
cmlenz@53: """, str(tmpl.generate()))
cmlenz@53:
cmlenz@53: def test_when_with_strip(self):
cmlenz@53: """
cmlenz@53: Verify that a when directive with a strip directive actually strips of
cmlenz@53: the outer element.
cmlenz@53: """
cmlenz@61: tmpl = Template("""
cmlenz@53:
cmlenz@53: foo
cmlenz@53:
cmlenz@53: """)
cmlenz@53: self.assertEqual("""
cmlenz@53: foo
cmlenz@53: """, str(tmpl.generate()))
cmlenz@53:
cmlenz@65: def test_as_element(self):
cmlenz@65: """
cmlenz@65: Verify that the directive can also be used as an element.
cmlenz@65: """
cmlenz@65: tmpl = Template("""
cmlenz@65:
cmlenz@65: 1
cmlenz@65: 2
cmlenz@65: 3
cmlenz@65:
cmlenz@65: """)
cmlenz@65: self.assertEqual("""
cmlenz@65: 1
cmlenz@65: """, str(tmpl.generate()))
cmlenz@65:
cmlenz@53:
cmlenz@50: class DefDirectiveTestCase(unittest.TestCase):
cmlenz@50: """Tests for the `py:def` template directive."""
cmlenz@50:
cmlenz@50: def test_function_with_strip(self):
cmlenz@50: """
cmlenz@53: Verify that a named template function with a strip directive actually
cmlenz@53: strips of the outer element.
cmlenz@50: """
cmlenz@61: tmpl = Template("""
cmlenz@50:
cmlenz@50: ${what}
cmlenz@50:
cmlenz@50: ${echo('foo')}
cmlenz@50: """)
cmlenz@50: self.assertEqual("""
cmlenz@50: foo
cmlenz@50: """, str(tmpl.generate()))
cmlenz@50:
cmlenz@90: def test_exec_in_replace(self):
cmlenz@90: tmpl = Template("""
cmlenz@90:
cmlenz@90: ${greeting}, ${name}!
cmlenz@90:
cmlenz@90:
cmlenz@90:
""")
cmlenz@90: self.assertEqual("""
cmlenz@90:
cmlenz@90: hello, world!
cmlenz@90:
cmlenz@90:
""", str(tmpl.generate()))
cmlenz@90:
cmlenz@65: def test_as_element(self):
cmlenz@65: """
cmlenz@65: Verify that the directive can also be used as an element.
cmlenz@65: """
cmlenz@65: tmpl = Template("""
cmlenz@65:
cmlenz@65: ${what}
cmlenz@65:
cmlenz@65: ${echo('foo')}
cmlenz@65: """)
cmlenz@65: self.assertEqual("""
cmlenz@65: foo
cmlenz@65: """, str(tmpl.generate()))
cmlenz@65:
cmlenz@50:
cmlenz@51: class ForDirectiveTestCase(unittest.TestCase):
cmlenz@53: """Tests for the `py:for` template directive."""
cmlenz@51:
cmlenz@51: def test_loop_with_strip(self):
cmlenz@51: """
cmlenz@53: Verify that the combining the `py:for` directive with `py:strip` works
cmlenz@53: correctly.
cmlenz@51: """
cmlenz@61: tmpl = Template("""
cmlenz@51:
cmlenz@51: ${item}
cmlenz@51:
cmlenz@51: """)
cmlenz@51: self.assertEqual("""
cmlenz@51: 1
cmlenz@51: 2
cmlenz@51: 3
cmlenz@51: 4
cmlenz@51: 5
cmlenz@51: """, str(tmpl.generate(Context(items=range(1, 6)))))
cmlenz@51:
cmlenz@65: def test_as_element(self):
cmlenz@65: """
cmlenz@65: Verify that the directive can also be used as an element.
cmlenz@65: """
cmlenz@65: tmpl = Template("""
cmlenz@65:
cmlenz@65: ${item}
cmlenz@65:
cmlenz@65: """)
cmlenz@65: self.assertEqual("""
cmlenz@65: 1
cmlenz@65: 2
cmlenz@65: 3
cmlenz@65: 4
cmlenz@65: 5
cmlenz@65: """, str(tmpl.generate(Context(items=range(1, 6)))))
cmlenz@65:
cmlenz@65:
cmlenz@65: class IfDirectiveTestCase(unittest.TestCase):
cmlenz@65: """Tests for the `py:if` template directive."""
cmlenz@65:
cmlenz@65: def test_loop_with_strip(self):
cmlenz@65: """
cmlenz@65: Verify that the combining the `py:if` directive with `py:strip` works
cmlenz@65: correctly.
cmlenz@65: """
cmlenz@65: tmpl = Template("""
cmlenz@65: ${bar}
cmlenz@65: """)
cmlenz@65: self.assertEqual("""
cmlenz@65: Hello
cmlenz@65: """, str(tmpl.generate(Context(foo=True, bar='Hello'))))
cmlenz@65:
cmlenz@65: def test_as_element(self):
cmlenz@65: """
cmlenz@65: Verify that the directive can also be used as an element.
cmlenz@65: """
cmlenz@65: tmpl = Template("""
cmlenz@65: ${bar}
cmlenz@65: """)
cmlenz@65: self.assertEqual("""
cmlenz@65: Hello
cmlenz@65: """, str(tmpl.generate(Context(foo=True, bar='Hello'))))
cmlenz@65:
cmlenz@51:
cmlenz@36: class MatchDirectiveTestCase(unittest.TestCase):
cmlenz@36: """Tests for the `py:match` template directive."""
cmlenz@36:
cmlenz@50: def test_with_strip(self):
cmlenz@50: """
cmlenz@50: Verify that a match template can produce the same kind of element that
cmlenz@50: it matched without entering an infinite recursion.
cmlenz@50: """
cmlenz@61: tmpl = Template("""
cmlenz@50:
cmlenz@50: ${select('*/text()')}
cmlenz@50:
cmlenz@50: Hey Joe
cmlenz@50: """)
cmlenz@50: self.assertEqual("""
cmlenz@50: Hey Joe
cmlenz@50: """, str(tmpl.generate()))
cmlenz@50:
cmlenz@36: def test_without_strip(self):
cmlenz@36: """
cmlenz@36: Verify that a match template can produce the same kind of element that
cmlenz@36: it matched without entering an infinite recursion.
cmlenz@36: """
cmlenz@61: tmpl = Template("""
cmlenz@36:
cmlenz@36: ${select('*/text()')}
cmlenz@36:
cmlenz@36: Hey Joe
cmlenz@37: """)
cmlenz@36: self.assertEqual("""
cmlenz@36:
cmlenz@36: Hey Joe
cmlenz@36:
cmlenz@36: """, str(tmpl.generate()))
cmlenz@36:
cmlenz@65: def test_as_element(self):
cmlenz@65: """
cmlenz@65: Verify that the directive can also be used as an element.
cmlenz@65: """
cmlenz@65: tmpl = Template("""
cmlenz@65:
cmlenz@65: ${select('*/text()')}
cmlenz@65:
cmlenz@65: Hey Joe
cmlenz@65: """)
cmlenz@65: self.assertEqual("""
cmlenz@65: Hey Joe
cmlenz@65: """, str(tmpl.generate()))
cmlenz@65:
cmlenz@36: def test_recursive_match_1(self):
cmlenz@36: """
cmlenz@36: Match directives are applied recursively, meaning that they are also
cmlenz@36: applied to any content they may have produced themselves:
cmlenz@36: """
cmlenz@61: tmpl = Template("""
cmlenz@36:
cmlenz@36:
cmlenz@36: ${select('*/*')}
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@37: """)
cmlenz@36: self.assertEqual("""
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36: """, str(tmpl.generate()))
cmlenz@36:
cmlenz@36: def test_recursive_match_2(self):
cmlenz@36: """
cmlenz@36: When two or more match templates match the same element and also
cmlenz@36: themselves output the element they match, avoiding recursion is even
cmlenz@36: more complex, but should work.
cmlenz@36: """
cmlenz@61: tmpl = Template("""
cmlenz@36:
cmlenz@36:
cmlenz@36: ${select('*/*')}
cmlenz@36:
cmlenz@36:
cmlenz@36: ${select('*/*')}
cmlenz@36:
cmlenz@36:
cmlenz@36:
cmlenz@36: Foo
cmlenz@36:
cmlenz@37: """)
cmlenz@36: self.assertEqual("""
cmlenz@36:
cmlenz@36: Foo
cmlenz@36:
cmlenz@36:
cmlenz@36: """, str(tmpl.generate()))
cmlenz@36:
cmlenz@77: def test_select_all_attrs(self):
cmlenz@77: tmpl = Template("""
cmlenz@77:
cmlenz@77: ${select('*/text()')}
cmlenz@77:
cmlenz@77: Hey Joe
cmlenz@77: """)
cmlenz@77: self.assertEqual("""
cmlenz@77:
cmlenz@77: Hey Joe
cmlenz@77:
cmlenz@77: """, str(tmpl.generate()))
cmlenz@77:
cmlenz@77: def test_select_all_attrs_empty(self):
cmlenz@77: tmpl = Template("""
cmlenz@77:
cmlenz@77: ${select('*/text()')}
cmlenz@77:
cmlenz@77: Hey Joe
cmlenz@77: """)
cmlenz@77: self.assertEqual("""
cmlenz@77:
cmlenz@77: Hey Joe
cmlenz@77:
cmlenz@77: """, str(tmpl.generate()))
cmlenz@77:
cmlenz@77: def test_select_all_attrs_in_body(self):
cmlenz@77: tmpl = Template("""
cmlenz@77:
cmlenz@77: Hey ${select('text()')} ${select('@*')}
cmlenz@77:
cmlenz@77: Joe
cmlenz@77: """)
cmlenz@77: self.assertEqual("""
cmlenz@77:
cmlenz@77: Hey Joe Cool
cmlenz@77:
cmlenz@77: """, str(tmpl.generate()))
cmlenz@77:
cmlenz@36:
cmlenz@37: class StripDirectiveTestCase(unittest.TestCase):
cmlenz@37: """Tests for the `py:strip` template directive."""
cmlenz@37:
cmlenz@37: def test_strip_false(self):
cmlenz@61: tmpl = Template("""
cmlenz@37:
foo
cmlenz@37:
""")
cmlenz@37: self.assertEqual("""
cmlenz@37:
foo
cmlenz@37:
""", str(tmpl.generate()))
cmlenz@37:
cmlenz@37: def test_strip_empty(self):
cmlenz@61: tmpl = Template("""
cmlenz@37:
foo
cmlenz@37:
""")
cmlenz@37: self.assertEqual("""
cmlenz@37: foo
cmlenz@37:
""", str(tmpl.generate()))
cmlenz@37:
cmlenz@37:
cmlenz@104: class WithDirectiveTestCase(unittest.TestCase):
cmlenz@104: """Tests for the `py:with` template directive."""
cmlenz@104:
cmlenz@104: def test_shadowing(self):
cmlenz@104: tmpl = Template("""
cmlenz@104: ${x}
cmlenz@104:
cmlenz@104: ${x}
cmlenz@104:
""")
cmlenz@104: self.assertEqual("""
cmlenz@104: 42
cmlenz@104: 84
cmlenz@104: 42
cmlenz@104:
""", str(tmpl.generate(Context(x=42))))
cmlenz@104:
cmlenz@104: def test_as_element(self):
cmlenz@104: tmpl = Template("""
cmlenz@104:
${x}
cmlenz@104:
""")
cmlenz@104: self.assertEqual("""
cmlenz@104: 84
cmlenz@104:
""", str(tmpl.generate(Context(x=42))))
cmlenz@104:
cmlenz@104:
cmlenz@1: class TemplateTestCase(unittest.TestCase):
cmlenz@36: """Tests for basic template processing, expression evaluation and error
cmlenz@36: reporting.
cmlenz@36: """
cmlenz@1:
cmlenz@1: def test_interpolate_string(self):
cmlenz@1: parts = list(Template._interpolate('bla'))
cmlenz@1: self.assertEqual(1, len(parts))
cmlenz@1: self.assertEqual(Stream.TEXT, parts[0][0])
cmlenz@1: self.assertEqual('bla', parts[0][1])
cmlenz@1:
cmlenz@1: def test_interpolate_simple(self):
cmlenz@1: parts = list(Template._interpolate('${bla}'))
cmlenz@1: self.assertEqual(1, len(parts))
cmlenz@10: self.assertEqual(Template.EXPR, parts[0][0])
cmlenz@1: self.assertEqual('bla', parts[0][1].source)
cmlenz@1:
cmlenz@1: def test_interpolate_escaped(self):
cmlenz@1: parts = list(Template._interpolate('$${bla}'))
cmlenz@1: self.assertEqual(1, len(parts))
cmlenz@1: self.assertEqual(Stream.TEXT, parts[0][0])
cmlenz@1: self.assertEqual('${bla}', parts[0][1])
cmlenz@1:
cmlenz@1: def test_interpolate_short(self):
cmlenz@1: parts = list(Template._interpolate('$bla'))
cmlenz@1: self.assertEqual(1, len(parts))
cmlenz@10: self.assertEqual(Template.EXPR, parts[0][0])
cmlenz@1: self.assertEqual('bla', parts[0][1].source)
cmlenz@1:
cmlenz@1: def test_interpolate_mixed1(self):
cmlenz@1: parts = list(Template._interpolate('$foo bar $baz'))
cmlenz@1: self.assertEqual(3, len(parts))
cmlenz@10: self.assertEqual(Template.EXPR, parts[0][0])
cmlenz@1: self.assertEqual('foo', parts[0][1].source)
cmlenz@1: self.assertEqual(Stream.TEXT, parts[1][0])
cmlenz@1: self.assertEqual(' bar ', parts[1][1])
cmlenz@10: self.assertEqual(Template.EXPR, parts[2][0])
cmlenz@1: self.assertEqual('baz', parts[2][1].source)
cmlenz@1:
cmlenz@1: def test_interpolate_mixed2(self):
cmlenz@1: parts = list(Template._interpolate('foo $bar baz'))
cmlenz@1: self.assertEqual(3, len(parts))
cmlenz@1: self.assertEqual(Stream.TEXT, parts[0][0])
cmlenz@1: self.assertEqual('foo ', parts[0][1])
cmlenz@10: self.assertEqual(Template.EXPR, parts[1][0])
cmlenz@1: self.assertEqual('bar', parts[1][1].source)
cmlenz@1: self.assertEqual(Stream.TEXT, parts[2][0])
cmlenz@1: self.assertEqual(' baz', parts[2][1])
cmlenz@1:
cmlenz@74: def test_interpolate_mixed3(self):
cmlenz@74: ctxt = Context(var=42)
cmlenz@74: tmpl = Template(' ${var} $var')
cmlenz@74: self.assertEqual(' 42 42', str(tmpl.generate(ctxt)))
cmlenz@74:
mgood@48: def test_interpolate_non_string_attrs(self):
mgood@48: tmpl = Template('')
cmlenz@75: self.assertEqual('', str(tmpl.generate()))
cmlenz@75:
cmlenz@75: def test_empty_attr(self):
cmlenz@75: tmpl = Template('')
cmlenz@75: self.assertEqual('', str(tmpl.generate()))
mgood@48:
cmlenz@1: def test_bad_directive_error(self):
cmlenz@61: xml = ''
cmlenz@1: try:
cmlenz@21: tmpl = Template(xml, filename='test.html')
cmlenz@1: except BadDirectiveError, e:
cmlenz@1: self.assertEqual('test.html', e.filename)
cmlenz@1: if sys.version_info[:2] >= (2, 4):
cmlenz@1: self.assertEqual(1, e.lineno)
cmlenz@1:
cmlenz@1: def test_directive_value_syntax_error(self):
cmlenz@81: xml = """"""
cmlenz@1: try:
cmlenz@81: tmpl = Template(xml, filename='test.html')
cmlenz@1: self.fail('Expected SyntaxError')
cmlenz@1: except TemplateSyntaxError, e:
cmlenz@1: self.assertEqual('test.html', e.filename)
cmlenz@1: if sys.version_info[:2] >= (2, 4):
cmlenz@1: self.assertEqual(1, e.lineno)
cmlenz@1: # We don't really care about the offset here, do we?
cmlenz@1:
cmlenz@1: def test_expression_syntax_error(self):
cmlenz@81: xml = """
cmlenz@81: Foo ${bar"}
cmlenz@81:
"""
cmlenz@1: try:
cmlenz@81: tmpl = Template(xml, filename='test.html')
cmlenz@1: self.fail('Expected SyntaxError')
cmlenz@1: except TemplateSyntaxError, e:
cmlenz@1: self.assertEqual('test.html', e.filename)
cmlenz@1: if sys.version_info[:2] >= (2, 4):
cmlenz@1: self.assertEqual(2, e.lineno)
cmlenz@1:
cmlenz@54: def test_markup_noescape(self):
cmlenz@54: """
cmlenz@54: Verify that outputting context data that is a `Markup` instance is not
cmlenz@54: escaped.
cmlenz@54: """
cmlenz@61: tmpl = Template("""
cmlenz@54: $myvar
cmlenz@54:
""")
cmlenz@54: self.assertEqual("""
cmlenz@54: foo
cmlenz@54:
""", str(tmpl.generate(Context(myvar=Markup('foo')))))
cmlenz@54:
cmlenz@54: def test_text_noescape_quotes(self):
cmlenz@54: """
cmlenz@54: Verify that outputting context data in text nodes doesn't escape quotes.
cmlenz@54: """
cmlenz@61: tmpl = Template("""
cmlenz@54: $myvar
cmlenz@54:
""")
cmlenz@54: self.assertEqual("""
cmlenz@54: "foo"
cmlenz@54:
""", str(tmpl.generate(Context(myvar='"foo"'))))
cmlenz@54:
cmlenz@54: def test_attr_escape_quotes(self):
cmlenz@54: """
cmlenz@54: Verify that outputting context data in attribtes escapes quotes.
cmlenz@54: """
cmlenz@61: tmpl = Template("""
cmlenz@54:
cmlenz@54:
""")
cmlenz@54: self.assertEqual("""
cmlenz@54:
cmlenz@54:
""", str(tmpl.generate(Context(myvar='"foo"'))))
cmlenz@54:
cmlenz@65: def test_directive_element(self):
cmlenz@65: tmpl = Template("""
cmlenz@65:
bar
cmlenz@65:
""")
cmlenz@65: self.assertEqual("""
cmlenz@65: bar
cmlenz@65:
""", str(tmpl.generate(Context(myvar='"foo"'))))
cmlenz@65:
cmlenz@89: def test_normal_comment(self):
cmlenz@89: tmpl = Template("""
cmlenz@89:
cmlenz@89:
""")
cmlenz@89: self.assertEqual("""
cmlenz@89:
cmlenz@89:
""", str(tmpl.generate()))
cmlenz@89:
cmlenz@89: def test_template_comment(self):
cmlenz@89: tmpl = Template("""
cmlenz@89:
cmlenz@89:
cmlenz@89:
""")
cmlenz@89: self.assertEqual("""
cmlenz@89:
""", str(tmpl.generate()))
cmlenz@89:
cmlenz@1:
cmlenz@1: def suite():
cmlenz@1: suite = unittest.TestSuite()
cmlenz@1: suite.addTest(doctest.DocTestSuite(Template.__module__))
cmlenz@1: suite.addTest(unittest.makeSuite(TemplateTestCase, 'test'))
cmlenz@50: suite.addTest(unittest.makeSuite(AttrsDirectiveTestCase, 'test'))
cmlenz@53: suite.addTest(unittest.makeSuite(ChooseDirectiveTestCase, 'test'))
cmlenz@50: suite.addTest(unittest.makeSuite(DefDirectiveTestCase, 'test'))
cmlenz@51: suite.addTest(unittest.makeSuite(ForDirectiveTestCase, 'test'))
cmlenz@65: suite.addTest(unittest.makeSuite(IfDirectiveTestCase, 'test'))
cmlenz@36: suite.addTest(unittest.makeSuite(MatchDirectiveTestCase, 'test'))
cmlenz@37: suite.addTest(unittest.makeSuite(StripDirectiveTestCase, 'test'))
cmlenz@104: suite.addTest(unittest.makeSuite(WithDirectiveTestCase, 'test'))
cmlenz@1: return suite
cmlenz@1:
cmlenz@1: if __name__ == '__main__':
cmlenz@1: unittest.main(defaultTest='suite')