[Zope3-checkins] CVS: Zope3/src/zope/tal/tests - __init__.py:1.2 markbench.py:1.2 run.py:1.2 test_files.py:1.2 test_htmltalparser.py:1.2 test_sourcepos.py:1.2 test_talinterpreter.py:1.2 test_xmlparser.py:1.2 utils.py:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:16:03 -0500


Update of /cvs-repository/Zope3/src/zope/tal/tests
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/tal/tests

Added Files:
	__init__.py markbench.py run.py test_files.py 
	test_htmltalparser.py test_sourcepos.py test_talinterpreter.py 
	test_xmlparser.py utils.py 
Log Message:
Grand renaming:

- Renamed most files (especially python modules) to lower case.

- Moved views and interfaces into separate hierarchies within each
  project, where each top-level directory under the zope package
  is a separate project.

- Moved everything to src from lib/python.

  lib/python will eventually go away. I need access to the cvs
  repository to make this happen, however.

There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.



=== Zope3/src/zope/tal/tests/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:02 2002
+++ Zope3/src/zope/tal/tests/__init__.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.


=== Zope3/src/zope/tal/tests/markbench.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:02 2002
+++ Zope3/src/zope/tal/tests/markbench.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,138 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+'''Run benchmarks of TAL vs. DTML'''
+
+import warnings
+warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+import os
+os.environ['NO_SECURITY'] = 'true'
+
+import sys
+import time
+
+from cStringIO import StringIO
+
+#from zope.documenttemplate.dt_html import HTMLFile
+
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.talinterpreter import TALInterpreter
+from zope.tal.dummyengine import DummyEngine
+
+
+def time_apply(f, args, kwargs, count):
+    r = [None] * count
+    for i in range(4):
+        f(*args, **kwargs)
+    t0 = time.clock()
+    for i in r:
+        pass
+    t1 = time.clock()
+    for i in r:
+        f(*args, **kwargs)
+    t = time.clock() - t1 - (t1 - t0)
+    return t / count
+
+def time_zpt(fn, count):
+    from zope.pagetemplate.pagetemplate import PageTemplate
+    pt = PageTemplate()
+    pt.write(open(fn).read())
+    return time_apply(pt.pt_render, (data,), {}, count)
+
+def time_tal(fn, count):
+    p = HTMLTALParser()
+    p.parseFile(fn)
+    program, macros = p.getCode()
+    engine = DummyEngine(macros)
+    engine.globals = data
+    tal = TALInterpreter(program, macros, engine, StringIO(), wrap=0,
+                         tal=1, strictinsert=0)
+    return time_apply(tal, (), {}, count)
+
+def time_dtml(fn, count):
+    html = HTMLFile(fn)
+    return time_apply(html, (), data, count)
+
+def profile_zpt(fn, count, profiler):
+    from zope.pagetemplate.pagetemplate import PageTemplate
+    pt = PageTemplate()
+    pt.write(open(fn).read())
+    for i in range(4):
+        pt.pt_render(extra_context=data)
+    r = [None] * count
+    for i in r:
+        profiler.runcall(pt.pt_render, 0, data)
+
+def profile_tal(fn, count, profiler):
+    p = HTMLTALParser()
+    p.parseFile(fn)
+    program, macros = p.getCode()
+    engine = DummyEngine(macros)
+    engine.globals = data
+    tal = TALInterpreter(program, macros, engine, StringIO(), wrap=0,
+                         tal=1, strictinsert=0)
+    for i in range(4):
+        tal()
+    r = [None] * count
+    for i in r:
+        profiler.runcall(tal)
+
+tal_fn = 'benchmark/tal%.2d.html'
+dtml_fn = 'benchmark/dtml%.2d.html'
+
+def compare(n, count, profiler=None):
+    t1 = int(time_zpt(tal_fn % n, count) * 1000 + 0.5)
+    t2 = int(time_tal(tal_fn % n, count) * 1000 + 0.5)
+    t3 = 'n/a' # int(time_dtml(dtml_fn % n, count) * 1000 + 0.5)
+    print '%.2d: %10s %10s %10s' % (n, t1, t2, t3)
+    if profiler:
+        profile_tal(tal_fn % n, count, profiler)
+
+def main(count, profiler=None):
+    n = 1
+    print '##: %10s %10s %10s' % ('ZPT', 'TAL', 'DTML')
+    while os.path.isfile(tal_fn % n) and os.path.isfile(dtml_fn % n):
+        compare(n, count, profiler)
+        n = n + 1
+
+data = {'x':'X', 'r2': range(2), 'r8': range(8), 'r64': range(64)}
+for i in range(10):
+    data['x%s' % i] = 'X%s' % i
+
+if __name__ == "__main__":
+    filename = "markbench.prof"
+    profiler = None
+    if len(sys.argv) > 1 and sys.argv[1] == "-p":
+        import profile
+        profiler = profile.Profile()
+        del sys.argv[1]
+
+    if len(sys.argv) > 1:
+        for arg in sys.argv[1:]:
+            compare(int(arg), 25, profiler)
+    else:
+        main(25, profiler)
+
+    if profiler is not None:
+        profiler.dump_stats(filename)
+        import pstats
+        p = pstats.Stats(filename)
+        p.strip_dirs()
+        p.sort_stats('time', 'calls')
+        try:
+            p.print_stats(20)
+        except IOError, e:
+            if e.errno != errno.EPIPE:
+                raise


=== Zope3/src/zope/tal/tests/run.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:02 2002
+++ Zope3/src/zope/tal/tests/run.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,42 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Run all tests."""
+
+import sys
+import unittest
+
+import zope.tal.tests.utils
+import zope.tal.tests.test_htmltalparser
+import zope.tal.tests.test_talinterpreter
+import zope.tal.tests.test_files
+import zope.tal.tests.test_sourcepos
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(test_htmltalparser.test_suite())
+    if not utils.skipxml:
+        import test_xmlparser
+        suite.addTest(test_xmlparser.test_suite())
+    suite.addTest(test_talinterpreter.test_suite())
+    suite.addTest(test_files.test_suite())
+    suite.addTest(test_sourcepos.test_suite())
+    return suite
+
+def main():
+    return utils.run_suite(test_suite())
+
+if __name__ == "__main__":
+    errs = main()
+    sys.exit(errs and 1 or 0)


=== Zope3/src/zope/tal/tests/test_files.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:02 2002
+++ Zope3/src/zope/tal/tests/test_files.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,86 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests that run driver.py over input files comparing to output files."""
+
+import glob
+import os
+import sys
+import unittest
+
+import zope.tal.runtest
+
+from zope.tal.tests import utils
+
+
+class FileTestCase(unittest.TestCase):
+
+    def __init__(self, file, dir):
+        self.__file = file
+        self.__dir = dir
+        unittest.TestCase.__init__(self)
+
+    # For unittest.
+    def shortDescription(self):
+        path = os.path.basename(self.__file)
+        return '%s (%s)' % (path, self.__class__)
+
+    def runTest(self):
+        basename = os.path.basename(self.__file)
+        #sys.stdout.write(basename + " ")
+        sys.stdout.flush()
+        if basename.startswith('test_metal'):
+            sys.argv = ["", "-Q", "-m", self.__file]
+        else:
+            sys.argv = ["", "-Q", self.__file]
+        pwd = os.getcwd()
+        try:
+            try:
+                os.chdir(self.__dir)
+                zope.tal.runtest.main()
+            finally:
+                os.chdir(pwd)
+        except SystemExit, what:
+            if what.code:
+                self.fail("output for %s didn't match" % self.__file)
+
+try:
+    script = __file__
+except NameError:
+    script = sys.argv[0]
+
+def test_suite():
+    suite = unittest.TestSuite()
+    dir = os.path.dirname(script)
+    dir = os.path.abspath(dir)
+    parentdir = os.path.dirname(dir)
+    prefix = os.path.join(dir, "input", "test*.")
+    if utils.skipxml:
+        xmlargs = []
+    else:
+        xmlargs = glob.glob(prefix + "xml")
+        xmlargs.sort()
+    htmlargs = glob.glob(prefix + "html")
+    htmlargs.sort()
+    args = xmlargs + htmlargs
+    if not args:
+        sys.stderr.write("Warning: no test input files found!!!\n")
+    for arg in args:
+        case = FileTestCase(arg, parentdir)
+        suite.addTest(case)
+    return suite
+
+if __name__ == "__main__":
+    errs = utils.run_suite(test_suite())
+    sys.exit(errs and 1 or 0)


=== Zope3/src/zope/tal/tests/test_htmltalparser.py 1.1 => 1.2 === (753/853 lines abridged)
--- /dev/null	Wed Dec 25 09:16:02 2002
+++ Zope3/src/zope/tal/tests/test_htmltalparser.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,850 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for the HTMLTALParser code generator."""
+
+import pprint
+import sys
+import unittest
+
+from zope.tal import htmltalparser
+from zope.tal import taldefs
+from zope.tal.tests import utils
+
+
+class TestCaseBase(unittest.TestCase):
+
+    prologue = ""
+    epilogue = ""
+    initial_program = [('version', taldefs.TAL_VERSION), ('mode', 'html')]
+    final_program = []
+
+    def _merge(self, p1, p2):
+        if p1 and p2:
+            op1, args1 = p1[-1]
+            op2, args2 = p2[0]
+            if op1.startswith('rawtext') and op2.startswith('rawtext'):
+                return (p1[:-1]
+                        + [rawtext(args1[0] + args2[0])]
+                        + p2[1:])
+        return p1+p2
+
+    def _run_check(self, source, program, macros={}):
+        parser = htmltalparser.HTMLTALParser()
+        parser.parseString(self.prologue + source + self.epilogue)
+        got_program, got_macros = parser.getCode()
+        program = self._merge(self.initial_program, program)

[-=- -=- -=- 753 lines omitted -=- -=- -=-]

+        # input/test27.html
+        self._run_check('''\
+<p i18n:translate="verify">Your contact email address is recorded as
+    <a href="mailto:user@example.com"
+       tal:content="request/submitter"
+       i18n:name="email">user@host.com</a>
+</p>
+''', [
+  ('setPosition', (1, 0)),
+  ('beginScope', {'i18n:translate': 'verify'}),
+  ('startTag', ('p', [('i18n:translate', 'verify', 'i18n')])),
+  ('insertTranslation',
+   ('verify',
+    [('rawtextBeginScope',
+      ('Your contact email address is recorded as\n    ',
+       4,
+       (2, 4),
+       0,
+       {'href': 'mailto:user@example.com',
+        'i18n:name': 'email',
+        'tal:content': 'request/submitter'})),
+     ('i18nVariable',
+      ('email',
+       [('startTag',
+         ('a',
+          [('href', 'href="mailto:user@example.com"'),
+           ('tal:content', 'request/submitter', 'tal'),
+           ('i18n:name', 'email', 'i18n')])),
+        ('insertText',
+         ('$request/submitter$',
+          [('rawtextOffset', ('user@host.com', 13))])),
+        ('rawtextOffset', ('</a>', 4))],
+       None)),
+     ('endScope', ()),
+     ('rawtextColumn', ('\n', 0))])),
+  ('endScope', ()),
+  ('rawtextColumn', ('</p>\n', 0))
+  ])
+
+
+def test_suite():
+    suite = unittest.makeSuite(HTMLTALParserTestCases)
+    suite.addTest(unittest.makeSuite(METALGeneratorTestCases))
+    suite.addTest(unittest.makeSuite(TALGeneratorTestCases))
+    return suite
+
+
+if __name__ == "__main__":
+    errs = utils.run_suite(test_suite())
+    sys.exit(errs and 1 or 0)


=== Zope3/src/zope/tal/tests/test_sourcepos.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:03 2002
+++ Zope3/src/zope/tal/tests/test_sourcepos.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,93 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for TALInterpreter."""
+
+import sys
+import unittest
+
+from StringIO import StringIO
+
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.talinterpreter import TALInterpreter
+from zope.tal.talgenerator import TALGenerator
+from zope.tal.dummyengine import DummyEngine
+
+
+page1 = '''<html metal:use-macro="main"><body>
+<div metal:fill-slot="body">
+page1=<span tal:replace="position:" />
+</div>
+</body></html>'''
+
+main_template = '''<html metal:define-macro="main"><body>
+main_template=<span tal:replace="position:" />
+<div metal:define-slot="body" />
+main_template=<span tal:replace="position:" />
+<div metal:use-macro="foot" />
+main_template=<span tal:replace="position:" />
+</body></html>'''
+
+footer = '''<div metal:define-macro="foot">
+footer=<span tal:replace="position:" />
+</div>'''
+
+expected = '''<html><body>
+main_template=main_template (2,14)
+<div>
+page1=page1 (3,6)
+</div>
+main_template=main_template (4,14)
+<div>
+footer=footer (2,7)
+</div>
+main_template=main_template (6,14)
+</body></html>'''
+
+
+
+class SourcePosTestCase(unittest.TestCase):
+
+    def parse(self, eng, s, fn):
+        gen = TALGenerator(expressionCompiler=eng, xml=0, source_file=fn)
+        parser = HTMLTALParser(gen)
+        parser.parseString(s)
+        program, macros = parser.getCode()
+        return program, macros
+
+    def test_source_positions(self):
+        # Ensure source file and position are set correctly by TAL
+        macros = {}
+        eng = DummyEngine(macros)
+        page1_program, page1_macros = self.parse(eng, page1, 'page1')
+        main_template_program, main_template_macros = self.parse(
+            eng, main_template, 'main_template')
+        footer_program, footer_macros = self.parse(eng, footer, 'footer')
+
+        macros['main'] = main_template_macros['main']
+        macros['foot'] = footer_macros['foot']
+
+        stream = StringIO()
+        interp = TALInterpreter(page1_program, macros, eng, stream)
+        interp()
+        self.assertEqual(stream.getvalue().strip(), expected.strip(),
+                         "Got result:\n%s\nExpected:\n%s"
+                         % (stream.getvalue(), expected))
+
+
+def test_suite():
+    return unittest.makeSuite(SourcePosTestCase)
+
+if __name__ == "__main__":
+    unittest.main(defaultTest='test_suite')


=== Zope3/src/zope/tal/tests/test_talinterpreter.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:03 2002
+++ Zope3/src/zope/tal/tests/test_talinterpreter.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,125 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for TALInterpreter."""
+
+import sys
+import unittest
+
+from StringIO import StringIO
+
+from zope.tal.taldefs import METALError, I18NError
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.talinterpreter import TALInterpreter
+from zope.tal.dummyengine import DummyEngine
+from zope.tal.tests import utils
+
+
+class TestCaseBase(unittest.TestCase):
+
+    def _compile(self, source):
+        parser = HTMLTALParser()
+        parser.parseString(source)
+        program, macros = parser.getCode()
+        return program, macros
+
+
+class MacroErrorsTestCase(TestCaseBase):
+
+    def setUp(self):
+        dummy, macros = self._compile('<p metal:define-macro="M">Booh</p>')
+        self.macro = macros['M']
+        self.engine = DummyEngine(macros)
+        program, dummy = self._compile('<p metal:use-macro="M">Bah</p>')
+        self.interpreter = TALInterpreter(program, {}, self.engine)
+
+    def tearDown(self):
+        try:
+            self.interpreter()
+        except METALError:
+            pass
+        else:
+            self.fail("Expected METALError")
+
+    def test_mode_error(self):
+        self.macro[1] = ("mode", "duh")
+
+    def test_version_error(self):
+        self.macro[0] = ("version", "duh")
+
+
+class I18NErrorsTestCase(TestCaseBase):
+
+    def _check(self, src, msg):
+        try:
+            self._compile(src)
+        except I18NError:
+            pass
+        else:
+            self.fail(msg)
+
+    def test_id_with_replace(self):
+        self._check('<p i18n:id="foo" tal:replace="string:splat"></p>',
+                    "expected i18n:id with tal:replace to be denied")
+
+    def test_missing_values(self):
+        self._check('<p i18n:attributes=""></p>',
+                    "missing i18n:attributes value not caught")
+        self._check('<p i18n:data=""></p>',
+                    "missing i18n:data value not caught")
+        self._check('<p i18n:id=""></p>',
+                    "missing i18n:id value not caught")
+
+
+class OutputPresentationTestCase(TestCaseBase):
+
+    def test_attribute_wrapping(self):
+        # To make sure the attribute-wrapping code is invoked, we have to
+        # include at least one TAL/METAL attribute to avoid having the start
+        # tag optimized into a rawtext instruction.
+        INPUT = r"""
+        <html this='element' has='a' lot='of' attributes=', so' the='output'
+              needs='to' be='line' wrapped='.' tal:define='foo nothing'>
+        </html>"""
+        EXPECTED = r'''
+        <html this="element" has="a" lot="of"
+              attributes=", so" the="output" needs="to"
+              be="line" wrapped=".">
+        </html>''' "\n"
+        self.compare(INPUT, EXPECTED)
+
+    def test_entities(self):
+        INPUT = ('<img tal:define="foo nothing" '
+                 'alt="&a; &#1; &#x0a; &a &#45 &; &#0a; <>" />')
+        EXPECTED = ('<img alt="&a; &#1; &#x0a; '
+                    '&amp;a &amp;#45 &amp;; &amp;#0a; &lt;&gt;" />\n')
+        self.compare(INPUT, EXPECTED)
+
+    def compare(self, INPUT, EXPECTED):
+        program, macros = self._compile(INPUT)
+        sio = StringIO()
+        interp = TALInterpreter(program, {}, DummyEngine(), sio, wrap=60)
+        interp()
+        self.assertEqual(sio.getvalue(), EXPECTED)
+
+
+def test_suite():
+    suite = unittest.makeSuite(I18NErrorsTestCase)
+    suite.addTest(unittest.makeSuite(MacroErrorsTestCase))
+    suite.addTest(unittest.makeSuite(OutputPresentationTestCase))
+    return suite
+
+if __name__ == "__main__":
+    errs = utils.run_suite(test_suite())
+    sys.exit(errs and 1 or 0)


=== Zope3/src/zope/tal/tests/test_xmlparser.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:03 2002
+++ Zope3/src/zope/tal/tests/test_xmlparser.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,261 @@
+#! /usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Tests for XMLParser.py."""
+
+import sys
+import unittest
+
+from zope.tal import xmlparser
+from zope.tal.tests import utils
+
+
+class EventCollector(xmlparser.XMLParser):
+
+    def __init__(self):
+        self.events = []
+        self.append = self.events.append
+        xmlparser.XMLParser.__init__(self)
+        self.parser.ordered_attributes = 1
+
+    def get_events(self):
+        # Normalize the list of events so that buffer artefacts don't
+        # separate runs of contiguous characters.
+        L = []
+        prevtype = None
+        for event in self.events:
+            type = event[0]
+            if type == prevtype == "data":
+                L[-1] = ("data", L[-1][1] + event[1])
+            else:
+                L.append(event)
+            prevtype = type
+        self.events = L
+        return L
+
+    # structure markup
+
+    def StartElementHandler(self, tag, attrs):
+        self.append(("starttag", tag, attrs))
+
+    def EndElementHandler(self, tag):
+        self.append(("endtag", tag))
+
+    # all other markup
+
+    def CommentHandler(self, data):
+        self.append(("comment", data))
+
+    def handle_charref(self, data):
+        self.append(("charref", data))
+
+    def CharacterDataHandler(self, data):
+        self.append(("data", data))
+
+    def StartDoctypeDeclHandler(self, rootelem, publicId, systemId, subset):
+        self.append(("doctype", rootelem, systemId, publicId, subset))
+
+    def XmlDeclHandler(self, version, encoding, standalone):
+        self.append(("decl", version, encoding, standalone))
+
+    def ExternalEntityRefHandler(self, data):
+        self.append(("entityref", data))
+
+    def ProcessingInstructionHandler(self, target, data):
+        self.append(("pi", target, data))
+
+
+class EventCollectorExtra(EventCollector):
+
+    def handle_starttag(self, tag, attrs):
+        EventCollector.handle_starttag(self, tag, attrs)
+        self.append(("starttag_text", self.get_starttag_text()))
+
+
+class SegmentedFile:
+    def __init__(self, parts):
+        self.parts = list(parts)
+
+    def read(self, bytes):
+        if self.parts:
+            s = self.parts.pop(0)
+        else:
+            s = ''
+        return s
+
+
+class XMLParserTestCase(unittest.TestCase):
+
+    def _run_check(self, source, events, collector=EventCollector):
+        parser = collector()
+        if isinstance(source, list):
+            parser.parseStream(SegmentedFile(source))
+        else:
+            parser.parseString(source)
+        self.assertEquals(parser.get_events(),events)
+
+    def _run_check_extra(self, source, events):
+        self._run_check(source, events, EventCollectorExtra)
+
+    def _parse_error(self, source):
+        def parse(source=source):
+            parser = xmlparser.XMLParser()
+            parser.parseString(source)
+        self.assertRaises(xmlparser.XMLParseError, parse)
+
+    def test_processing_instruction_plus(self):
+        self._run_check("<?processing instruction?><a/>", [
+            ("pi", "processing", "instruction"),
+            ("starttag", "a", []),
+            ("endtag", "a"),
+            ])
+
+    def _check_simple_html(self):
+        self._run_check("""\
+<?xml version='1.0' encoding='iso-8859-1'?>
+<!DOCTYPE html PUBLIC 'foo' 'bar'>
+<html>&entity;&#32;
+<!--comment1a
+-></foo><bar>&lt;<?pi?></foo<bar
+comment1b-->
+<img src='Bar' ismap=''/>sample
+text
+<!--comment2a- -comment2b-->
+</html>
+""", [
+    ("decl", "1.0", "iso-8859-1", -1),
+    ("doctype", "html", "foo", "bar", 0),
+    ("starttag", "html", []),
+#    ("entityref", "entity"),
+    ("data", " \n"),
+    ("comment", "comment1a\n-></foo><bar>&lt;<?pi?></foo<bar\ncomment1b"),
+    ("data", "\n"),
+    ("starttag", "img", ["src", "Bar", "ismap", ""]),
+    ("endtag", "img"),
+    ("data", "sample\ntext\n"),
+    ("comment", "comment2a- -comment2b"),
+    ("data", "\n"),
+    ("endtag", "html"),
+    ])
+
+    def test_bad_nesting(self):
+        try:
+            self._run_check("<a><b></a></b>", [
+                ("starttag", "a", []),
+                ("starttag", "b", []),
+                ("endtag", "a"),
+                ("endtag", "b"),
+                ])
+        except:
+            e = sys.exc_info()[1]
+            self.assert_(e.lineno == 1,
+                         "did not receive correct position information")
+        else:
+            self.fail("expected parse error: bad nesting")
+
+    def test_attr_syntax(self):
+        output = [
+          ("starttag", "a", ["b", "v", "c", "v"]),
+          ("endtag", "a"),
+          ]
+        self._run_check("""<a b='v' c="v"/>""", output)
+        self._run_check("""<a  b = 'v' c = "v"/>""", output)
+        self._run_check("""<a\nb\n=\n'v'\nc\n=\n"v"\n/>""", output)
+        self._run_check("""<a\tb\t=\t'v'\tc\t=\t"v"\t/>""", output)
+
+    def test_attr_values(self):
+        self._run_check("""<a b='xxx\n\txxx' c="yyy\t\nyyy" d='\txyz\n'/>""",
+                        [("starttag", "a", ["b", "xxx  xxx",
+                                            "c", "yyy  yyy",
+                                            "d", " xyz "]),
+                         ("endtag", "a"),
+                         ])
+        self._run_check("""<a b='' c="" d=''/>""", [
+            ("starttag", "a", ["b", "", "c", "", "d", ""]),
+            ("endtag", "a"),
+            ])
+
+    def test_attr_entity_replacement(self):
+        self._run_check("""<a b='&amp;&gt;&lt;&quot;&apos;'/>""", [
+            ("starttag", "a", ["b", "&><\"'"]),
+            ("endtag", "a"),
+            ])
+
+    def test_attr_funky_names(self):
+        self._run_check("""<a a.b='v' c:d='v' e-f='v'/>""", [
+            ("starttag", "a", ["a.b", "v", "c:d", "v", "e-f", "v"]),
+            ("endtag", "a"),
+            ])
+
+    def test_starttag_end_boundary(self):
+        self._run_check("""<a b='&lt;'/>""", [
+            ("starttag", "a", ["b", "<"]),
+            ("endtag", "a"),
+            ])
+        self._run_check("""<a b='&gt;'/>""", [
+            ("starttag", "a", ["b", ">"]),
+            ("endtag", "a"),
+            ])
+
+    def test_buffer_artefacts(self):
+        output = [("starttag", "a", ["b", "<"]), ("endtag", "a")]
+        self._run_check(["<a b='&lt;'/>"], output)
+        self._run_check(["<a ", "b='&lt;'/>"], output)
+        self._run_check(["<a b", "='&lt;'/>"], output)
+        self._run_check(["<a b=", "'&lt;'/>"], output)
+        self._run_check(["<a b='&lt;", "'/>"], output)
+        self._run_check(["<a b='&lt;'", "/>"], output)
+
+        output = [("starttag", "a", ["b", ">"]), ("endtag", "a")]
+        self._run_check(["<a b='&gt;'/>"], output)
+        self._run_check(["<a ", "b='&gt;'/>"], output)
+        self._run_check(["<a b", "='&gt;'/>"], output)
+        self._run_check(["<a b=", "'&gt;'/>"], output)
+        self._run_check(["<a b='&gt;", "'/>"], output)
+        self._run_check(["<a b='&gt;'", "/>"], output)
+
+    def test_starttag_junk_chars(self):
+        self._parse_error("<")
+        self._parse_error("<>")
+        self._parse_error("</>")
+        self._parse_error("</$>")
+        self._parse_error("</")
+        self._parse_error("</a")
+        self._parse_error("</a")
+        self._parse_error("<a<a>")
+        self._parse_error("</a<a>")
+        self._parse_error("<$")
+        self._parse_error("<$>")
+        self._parse_error("<!")
+        self._parse_error("<a $>")
+        self._parse_error("<a")
+        self._parse_error("<a foo='bar'")
+        self._parse_error("<a foo='bar")
+        self._parse_error("<a foo='>'")
+        self._parse_error("<a foo='>")
+
+    def test_declaration_junk_chars(self):
+        self._parse_error("<!DOCTYPE foo $ >")
+
+
+# Support for the Zope regression test framework:
+def test_suite(skipxml=utils.skipxml):
+    if skipxml:
+        return unittest.TestSuite()
+    else:
+        return unittest.makeSuite(XMLParserTestCase)
+
+if __name__ == "__main__":
+    errs = utils.run_suite(test_suite(skipxml=0))
+    sys.exit(errs and 1 or 0)


=== Zope3/src/zope/tal/tests/utils.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:16:03 2002
+++ Zope3/src/zope/tal/tests/utils.py	Wed Dec 25 09:15:31 2002
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Helper functions for the test suite."""
+
+import os
+import sys
+
+mydir = os.path.abspath(os.path.dirname(__file__))
+codedir = os.path.dirname(os.path.dirname(os.path.dirname(mydir)))
+
+if codedir not in sys.path:
+    sys.path.append(codedir)
+
+import unittest
+
+
+# Set skipxml to true if an XML parser could not be found.
+skipxml = 0
+try:
+    import xml.parsers.expat
+except ImportError:
+    skipxml = 1
+
+
+def run_suite(suite, outf=None, errf=None):
+    if outf is None:
+        outf = sys.stdout
+    runner = unittest.TextTestRunner(outf)
+    result = runner.run(suite)
+
+##     print "\n\n"
+##     if result.errors:
+##         print "Errors (unexpected exceptions):"
+##         map(print_error, result.errors)
+##         print
+##     if result.failures:
+##         print "Failures (assertion failures):"
+##         map(print_error, result.failures)
+##         print
+    newerrs = len(result.errors) + len(result.failures)
+    if newerrs:
+        print "'Errors' indicate exceptions other than AssertionError."
+        print "'Failures' indicate AssertionError"
+        if errf is None:
+            errf = sys.stderr
+        errf.write("%d errors, %d failures\n"
+                   % (len(result.errors), len(result.failures)))
+    return newerrs
+
+
+def print_error(info):
+    testcase, (type, e, tb) = info