summaryrefslogtreecommitdiff
path: root/src/zope/tal/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/zope/tal/tests')
-rw-r--r--src/zope/tal/tests/__init__.py2
-rw-r--r--src/zope/tal/tests/input/__init__.py2
-rw-r--r--src/zope/tal/tests/input/acme_template.pt15
-rw-r--r--src/zope/tal/tests/input/document_list.pt21
-rw-r--r--src/zope/tal/tests/input/pnome_template.pt23
-rw-r--r--src/zope/tal/tests/input/test01.html56
-rw-r--r--src/zope/tal/tests/input/test01.xml57
-rw-r--r--src/zope/tal/tests/input/test02.html118
-rw-r--r--src/zope/tal/tests/input/test02.xml119
-rw-r--r--src/zope/tal/tests/input/test03.html9
-rw-r--r--src/zope/tal/tests/input/test03.xml10
-rw-r--r--src/zope/tal/tests/input/test04.html26
-rw-r--r--src/zope/tal/tests/input/test04.xml27
-rw-r--r--src/zope/tal/tests/input/test05.html9
-rw-r--r--src/zope/tal/tests/input/test05.xml10
-rw-r--r--src/zope/tal/tests/input/test06.html6
-rw-r--r--src/zope/tal/tests/input/test06.xml7
-rw-r--r--src/zope/tal/tests/input/test07.html11
-rw-r--r--src/zope/tal/tests/input/test07.xml12
-rw-r--r--src/zope/tal/tests/input/test08.html44
-rw-r--r--src/zope/tal/tests/input/test08.xml45
-rw-r--r--src/zope/tal/tests/input/test09.html30
-rw-r--r--src/zope/tal/tests/input/test09.xml30
-rw-r--r--src/zope/tal/tests/input/test10.html48
-rw-r--r--src/zope/tal/tests/input/test11.html19
-rw-r--r--src/zope/tal/tests/input/test11.xml14
-rw-r--r--src/zope/tal/tests/input/test12.html24
-rw-r--r--src/zope/tal/tests/input/test13.html7
-rw-r--r--src/zope/tal/tests/input/test14.html10
-rw-r--r--src/zope/tal/tests/input/test14.xml15
-rw-r--r--src/zope/tal/tests/input/test15.html26
-rw-r--r--src/zope/tal/tests/input/test16.html2
-rw-r--r--src/zope/tal/tests/input/test16.xml7
-rw-r--r--src/zope/tal/tests/input/test17.html6
-rw-r--r--src/zope/tal/tests/input/test17.xml10
-rw-r--r--src/zope/tal/tests/input/test18.html16
-rw-r--r--src/zope/tal/tests/input/test18.xml20
-rw-r--r--src/zope/tal/tests/input/test19.html5
-rw-r--r--src/zope/tal/tests/input/test19.xml8
-rw-r--r--src/zope/tal/tests/input/test20.html1
-rw-r--r--src/zope/tal/tests/input/test20.xml6
-rw-r--r--src/zope/tal/tests/input/test21.html4
-rw-r--r--src/zope/tal/tests/input/test21.xml9
-rw-r--r--src/zope/tal/tests/input/test22.html4
-rw-r--r--src/zope/tal/tests/input/test22.xml6
-rw-r--r--src/zope/tal/tests/input/test23.html2
-rw-r--r--src/zope/tal/tests/input/test24.html12
-rw-r--r--src/zope/tal/tests/input/test25.html1
-rw-r--r--src/zope/tal/tests/input/test26.html3
-rw-r--r--src/zope/tal/tests/input/test27.html5
-rw-r--r--src/zope/tal/tests/input/test28.html5
-rw-r--r--src/zope/tal/tests/input/test29.html4
-rw-r--r--src/zope/tal/tests/input/test30.html6
-rw-r--r--src/zope/tal/tests/input/test31.html7
-rw-r--r--src/zope/tal/tests/input/test32.html4
-rw-r--r--src/zope/tal/tests/input/test33.html1
-rw-r--r--src/zope/tal/tests/input/test34.html11
-rw-r--r--src/zope/tal/tests/input/test35.html7
-rw-r--r--src/zope/tal/tests/input/test36.html6
-rw-r--r--src/zope/tal/tests/input/test_domain.html7
-rw-r--r--src/zope/tal/tests/input/test_failed_attr_translation.html2
-rw-r--r--src/zope/tal/tests/input/test_metal1.html61
-rw-r--r--src/zope/tal/tests/input/test_metal2.html7
-rw-r--r--src/zope/tal/tests/input/test_metal3.html1
-rw-r--r--src/zope/tal/tests/input/test_metal4.html4
-rw-r--r--src/zope/tal/tests/input/test_metal5.html4
-rw-r--r--src/zope/tal/tests/input/test_metal6.html5
-rw-r--r--src/zope/tal/tests/input/test_metal7.html6
-rw-r--r--src/zope/tal/tests/input/test_metal8.html15
-rw-r--r--src/zope/tal/tests/input/test_metal9.html23
-rw-r--r--src/zope/tal/tests/input/test_sa1.html6
-rw-r--r--src/zope/tal/tests/input/test_sa1.xml7
-rw-r--r--src/zope/tal/tests/input/test_sa2.html9
-rw-r--r--src/zope/tal/tests/input/test_sa2.xml10
-rw-r--r--src/zope/tal/tests/input/test_sa3.html15
-rw-r--r--src/zope/tal/tests/input/test_sa3.xml16
-rw-r--r--src/zope/tal/tests/input/test_sa4.html11
-rw-r--r--src/zope/tal/tests/markbench.py187
-rw-r--r--src/zope/tal/tests/output/__init__.py2
-rw-r--r--src/zope/tal/tests/output/acme_template.html26
-rw-r--r--src/zope/tal/tests/output/acme_template_source.html27
-rw-r--r--src/zope/tal/tests/output/document_list.html30
-rw-r--r--src/zope/tal/tests/output/document_list_source.html30
-rw-r--r--src/zope/tal/tests/output/test01.html68
-rw-r--r--src/zope/tal/tests/output/test01.xml65
-rw-r--r--src/zope/tal/tests/output/test02.html118
-rw-r--r--src/zope/tal/tests/output/test02.xml119
-rw-r--r--src/zope/tal/tests/output/test03.html9
-rw-r--r--src/zope/tal/tests/output/test03.xml10
-rw-r--r--src/zope/tal/tests/output/test04.html38
-rw-r--r--src/zope/tal/tests/output/test04.xml39
-rw-r--r--src/zope/tal/tests/output/test05.html9
-rw-r--r--src/zope/tal/tests/output/test05.xml10
-rw-r--r--src/zope/tal/tests/output/test06.html7
-rw-r--r--src/zope/tal/tests/output/test06.xml8
-rw-r--r--src/zope/tal/tests/output/test07.html11
-rw-r--r--src/zope/tal/tests/output/test07.xml12
-rw-r--r--src/zope/tal/tests/output/test08.html47
-rw-r--r--src/zope/tal/tests/output/test08.xml48
-rw-r--r--src/zope/tal/tests/output/test09.html30
-rw-r--r--src/zope/tal/tests/output/test09.xml30
-rw-r--r--src/zope/tal/tests/output/test10.html51
-rw-r--r--src/zope/tal/tests/output/test11.html8
-rw-r--r--src/zope/tal/tests/output/test11.xml5
-rw-r--r--src/zope/tal/tests/output/test12.html24
-rw-r--r--src/zope/tal/tests/output/test13.html7
-rw-r--r--src/zope/tal/tests/output/test14.html13
-rw-r--r--src/zope/tal/tests/output/test14.xml18
-rw-r--r--src/zope/tal/tests/output/test15.html29
-rw-r--r--src/zope/tal/tests/output/test16.html1
-rw-r--r--src/zope/tal/tests/output/test16.xml6
-rw-r--r--src/zope/tal/tests/output/test17.html6
-rw-r--r--src/zope/tal/tests/output/test17.xml9
-rw-r--r--src/zope/tal/tests/output/test18.html16
-rw-r--r--src/zope/tal/tests/output/test18.xml19
-rw-r--r--src/zope/tal/tests/output/test19.html3
-rw-r--r--src/zope/tal/tests/output/test19.xml6
-rw-r--r--src/zope/tal/tests/output/test20.html1
-rw-r--r--src/zope/tal/tests/output/test20.xml4
-rw-r--r--src/zope/tal/tests/output/test21.html1
-rw-r--r--src/zope/tal/tests/output/test21.xml4
-rw-r--r--src/zope/tal/tests/output/test22.html1
-rw-r--r--src/zope/tal/tests/output/test22.xml6
-rw-r--r--src/zope/tal/tests/output/test23.html1
-rw-r--r--src/zope/tal/tests/output/test24.html7
-rw-r--r--src/zope/tal/tests/output/test25.html1
-rw-r--r--src/zope/tal/tests/output/test26.html1
-rw-r--r--src/zope/tal/tests/output/test27.html1
-rw-r--r--src/zope/tal/tests/output/test28.html1
-rw-r--r--src/zope/tal/tests/output/test29.html1
-rw-r--r--src/zope/tal/tests/output/test30.html1
-rw-r--r--src/zope/tal/tests/output/test31.html1
-rw-r--r--src/zope/tal/tests/output/test32.html1
-rw-r--r--src/zope/tal/tests/output/test33.html1
-rw-r--r--src/zope/tal/tests/output/test34.html7
-rw-r--r--src/zope/tal/tests/output/test35.html6
-rw-r--r--src/zope/tal/tests/output/test36.html2
-rw-r--r--src/zope/tal/tests/output/test_domain.html5
-rw-r--r--src/zope/tal/tests/output/test_failed_attr_translation.html1
-rw-r--r--src/zope/tal/tests/output/test_metal1.html79
-rw-r--r--src/zope/tal/tests/output/test_metal2.html11
-rw-r--r--src/zope/tal/tests/output/test_metal3.html1
-rw-r--r--src/zope/tal/tests/output/test_metal4.html4
-rw-r--r--src/zope/tal/tests/output/test_metal5.html4
-rw-r--r--src/zope/tal/tests/output/test_metal6.html5
-rw-r--r--src/zope/tal/tests/output/test_metal7.html6
-rw-r--r--src/zope/tal/tests/output/test_metal8.html19
-rw-r--r--src/zope/tal/tests/output/test_metal9.html32
-rw-r--r--src/zope/tal/tests/output/test_sa1.html10
-rw-r--r--src/zope/tal/tests/output/test_sa1.xml11
-rw-r--r--src/zope/tal/tests/output/test_sa2.html13
-rw-r--r--src/zope/tal/tests/output/test_sa2.xml14
-rw-r--r--src/zope/tal/tests/output/test_sa3.html42
-rw-r--r--src/zope/tal/tests/output/test_sa3.xml43
-rw-r--r--src/zope/tal/tests/output/test_sa4.html30
-rw-r--r--src/zope/tal/tests/run.py45
-rw-r--r--src/zope/tal/tests/test_files.py90
-rw-r--r--src/zope/tal/tests/test_htmltalparser.py1021
-rw-r--r--src/zope/tal/tests/test_sourcepos.py93
-rw-r--r--src/zope/tal/tests/test_talgettext.py78
-rw-r--r--src/zope/tal/tests/test_talinterpreter.py853
-rw-r--r--src/zope/tal/tests/test_talparser.py39
-rw-r--r--src/zope/tal/tests/test_xmlparser.py268
-rw-r--r--src/zope/tal/tests/utils.py65
164 files changed, 5371 insertions, 0 deletions
diff --git a/src/zope/tal/tests/__init__.py b/src/zope/tal/tests/__init__.py
new file mode 100644
index 0000000..b711d36
--- /dev/null
+++ b/src/zope/tal/tests/__init__.py
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
diff --git a/src/zope/tal/tests/input/__init__.py b/src/zope/tal/tests/input/__init__.py
new file mode 100644
index 0000000..b711d36
--- /dev/null
+++ b/src/zope/tal/tests/input/__init__.py
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
diff --git a/src/zope/tal/tests/input/acme_template.pt b/src/zope/tal/tests/input/acme_template.pt
new file mode 100644
index 0000000..0af01ba
--- /dev/null
+++ b/src/zope/tal/tests/input/acme_template.pt
@@ -0,0 +1,15 @@
+<!-- This is ACME's generic look and feel, which is based on
+PNOME's look and feel. -->
+<html metal:extend-macro="pnome_macros_page" metal:define-macro="page">
+<head>
+<title metal:fill-slot="title">ACME Look and Feel</title>
+</head>
+<body>
+<div metal:fill-slot="page-footer">
+Copyright 2004 Acme Inc.
+<div metal:define-slot="disclaimer">
+Standard disclaimers apply.
+</div>
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/document_list.pt b/src/zope/tal/tests/input/document_list.pt
new file mode 100644
index 0000000..8226be1
--- /dev/null
+++ b/src/zope/tal/tests/input/document_list.pt
@@ -0,0 +1,21 @@
+<!-- ACME's document_list uses the ACME look and feel -->
+<html metal:use-macro="acme_macros_page">
+<head>
+<title metal:fill-slot="title">Acme Document List</title>
+<style metal:fill-slot="local-styles" type="text/css">
+ body { background-color: white; }
+</style>
+</head>
+<body>
+<div metal:fill-slot="content">
+<h1>Documents</h1>
+<ul>
+<li>Rocket Science for Dummies</li>
+<li>Birds for the Gourmet Chef</li>
+</ul>
+</div>
+<div metal:fill-slot="disclaimer">
+This document list is classified.
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/pnome_template.pt b/src/zope/tal/tests/input/pnome_template.pt
new file mode 100644
index 0000000..f4d1c66
--- /dev/null
+++ b/src/zope/tal/tests/input/pnome_template.pt
@@ -0,0 +1,23 @@
+<!-- fakeplone is a fictional user interface created by a large,
+well-focused team of graphics designers -->
+<html metal:define-macro="page">
+<head>
+<title metal:define-slot="title">Title here</title>
+<metal:block define-slot="local-styles">
+</metal:block>
+</head>
+<body>
+<div>
+ <div metal:define-slot="annoying-quote">
+ "The early bird gets the worm, but the second mouse gets the cheese."
+ </div>
+ <a href="#">Preferences...</a>
+</div>
+<div metal:define-slot="content">
+ Content here
+</div>
+<div metal:define-slot="page-footer">
+ page footer
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test01.html b/src/zope/tal/tests/input/test01.html
new file mode 100644
index 0000000..e2ae0c4
--- /dev/null
+++ b/src/zope/tal/tests/input/test01.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+
+ <head>dadada</head>
+
+ <body xmlns:z="http://xml.zope.org/namespaces/tal" z:define="foo python:1">
+<h1 z:condition="python:0">This title is not displayed</h1>
+ <h1 z:condition="python:1" z:content="str:This
+Is
+The
+Replaced
+Title">Title</h1>
+
+ <!-- test entity references -->
+ &nbsp;&HarryPotter;
+
+ <!-- examples adapted from TemplateAttributeLanguageSyntax -->
+
+ <span z:content="str:here/id"/>
+
+ <p z:define="x str:template/title; global five python:2+3;" z:content="text var:five"/>
+
+ <p z:repeat="car python:['honda', 'subaru', 'acura']">
+ <span z:replace="var:car"/>
+ </p>
+
+ <p xml:foo="bar">foo bar</p>
+
+ <!-- more examples -->
+
+ <ul>
+ <span z:repeat="car python:['honda', 'subaru', 'acura']">
+ <li z:content="var:car">Car Name</li>
+ </span>
+ </ul>
+
+ <!-- test attribute expansion -->
+
+ <a href="foo" z:attributes="href python:'http://python.org' ">python</a>
+ <a z:attributes="href python:'http://python.org' ">python</a>
+
+ <!-- test insert/replace structure -->
+ <span z:content="structure python:None" />
+ <span z:replace="structure python:None" />
+
+ <span z:define="global x str:&lt;h3&gt;Header Level 3&lt;/h3&gt;" />
+ <span z:define="global x python:'&amp;' + 'nbsp;;' + x" />
+
+ <span z:replace="structure x" />
+ <span z:content="structure x" />
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/input/test01.xml b/src/zope/tal/tests/input/test01.xml
new file mode 100644
index 0000000..82038e9
--- /dev/null
+++ b/src/zope/tal/tests/input/test01.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+
+ <head>dadada</head>
+
+ <body xmlns:z="http://xml.zope.org/namespaces/tal" z:define="foo python:1">
+<h1 z:condition="python:0">This title is not displayed</h1>
+ <h1 z:condition="python:1" z:content="str:This
+Is
+The
+Replaced
+Title">Title</h1>
+
+ <!-- test entity references -->
+ &nbsp;&HarryPotter;
+
+ <!-- examples adapted from TemplateAttributeLanguageSyntax -->
+
+ <span z:content="str:here/id"/>
+
+ <p z:define="x str:template/title; global five python:2+3;" z:content="text var:five"/>
+
+ <p z:repeat="car python:['honda', 'subaru', 'acura']">
+ <span z:replace="var:car"/>
+ </p>
+
+ <p xml:foo="bar">foo bar</p>
+
+ <!-- more examples -->
+
+ <ul>
+ <span z:repeat="car python:['honda', 'subaru', 'acura']">
+ <li z:content="var:car">Car Name</li>
+ </span>
+ </ul>
+
+ <!-- test attribute expansion -->
+
+ <a href="foo" z:attributes="href python:'http://python.org' ">python</a>
+ <a z:attributes="href python:'http://python.org' ">python</a>
+
+ <!-- test insert/replace structure -->
+ <span z:content="structure python:None" />
+ <span z:replace="structure python:None" />
+
+ <span z:define="global x str:&lt;h3&gt;Header Level 3&lt;/h3&gt;" />
+ <span z:define="global x python:'&amp;' + 'nbsp;;' + x" />
+
+ <span z:replace="structure x" />
+ <span z:content="structure x" />
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/input/test02.html b/src/zope/tal/tests/input/test02.html
new file mode 100644
index 0000000..df2fb18
--- /dev/null
+++ b/src/zope/tal/tests/input/test02.html
@@ -0,0 +1,118 @@
+<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
+
+<foo:header xmlns:foo="whomping-willow" plain="guido" quote='"' apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
+ <manifest>
+ <document>
+ <name>sample1</name>
+ <description>a simple invoice</description>
+ </document>
+ </manifest>
+</foo:header>
+
+<body>
+
+<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
+
+<Invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
+ <Header>
+ <InvoiceNumber>01786</InvoiceNumber>
+ <InvoiceDate>2000-03-17</InvoiceDate> <!-- March 17th, 2000 -->
+ <OrderNo>55377</OrderNo>
+ <OrderDate>2000-03-15</OrderDate> <!-- March 15th, 2000 -->
+ <CustomerPO>GJ03405</CustomerPO>
+ <ShipMethod>DAVE 1</ShipMethod>
+ <ShipDate>2000-03-17</ShipDate> <!-- March 17th, 2000 -->
+ <CustomerID>K5211(34)</CustomerID>
+ <SalesPersonCode>23</SalesPersonCode>
+ <TaxID>23</TaxID>
+ </Header>
+ <InvoiceTo>
+ <Name>SHIPWRIGHT RESTAURANTS LIMITED</Name>
+ <AddressLine>125 NORTH SERVICE ROAD W</AddressLine>
+ <AddressLine>WESTLAKE ACCESS</AddressLine>
+ <City>NORTH BAY</City>
+ <PostCode>L8B1O5</PostCode>
+ <State>ONTARIO</State>
+ <Country>CANADA</Country>
+ </InvoiceTo>
+ <ShipTo>
+ <Name/>
+ <AddressLine>ATTN: PAULINE DEGRASSI</AddressLine>
+ <City/>
+ <PostCode/>
+ <State/>
+ <Country/>
+ </ShipTo>
+ <DetailLines>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>CS</UnitOfMeasure>
+ <PartNumber>DM 5309</PartNumber>
+ <PartDescription>#1013 12 OZ.MUNICH STEIN</PartDescription>
+ <UnitPrice>37.72</UnitPrice>
+ <LineTotal>37.72</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>6</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6420</PartNumber>
+ <PartDescription>PROVINCIAL DINNER FORK</PartDescription>
+ <UnitPrice>17.98</UnitPrice>
+ <LineTotal>107.88</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>72</QuantityShipped>
+ <UnitOfMeasure>EA</UnitOfMeasure>
+ <PartNumber>JR20643</PartNumber>
+ <PartDescription>PLASTIC HANDLED STEAK KNIFE</PartDescription>
+ <UnitPrice>.81</UnitPrice>
+ <LineTotal>58.32</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>6</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6410</PartNumber>
+ <PartDescription>PROVINCIAL TEASPOONS</PartDescription>
+ <UnitPrice>12.16</UnitPrice>
+ <LineTotal>72.96</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>0</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6411</PartNumber>
+ <PartDescription>PROVINCIAL RD BOWL SPOON</PartDescription>
+ <QuantityBackOrdered>6</QuantityBackOrdered>
+ <UnitPrice>17.98</UnitPrice>
+ <LineTotal>0.00</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>EA</UnitOfMeasure>
+ <PartNumber>DO 3218</PartNumber>
+ <PartDescription>34 OZ DUAL DIAL SCALE AM3218</PartDescription>
+ <UnitPrice>70.00</UnitPrice>
+ <DiscountPercentage>5.0</DiscountPercentage>
+ <LineTotal>66.50</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>CS</UnitOfMeasure>
+ <PartNumber>DM 195</PartNumber>
+ <PartDescription>20 OZ.BEER PUB GLASS</PartDescription>
+ <UnitPrice>55.90</UnitPrice>
+ <LineTotal>55.90</LineTotal>
+ </DetailLine>
+ </DetailLines>
+ <Totals>
+ <SubTotal>399.28</SubTotal>
+ <DiscountTotal>3.50</DiscountTotal>
+ <FreightTotal>23.75</FreightTotal>
+ <GSTTotal>29.61</GSTTotal>
+ <ProvTaxTotal>33.84</ProvTaxTotal>
+ <OtherTotal>33.84</OtherTotal>
+ <InvoiceTotal>486.48</InvoiceTotal>
+ </Totals>
+</Invoice>
+
+</body>
+</biztalk_1>
diff --git a/src/zope/tal/tests/input/test02.xml b/src/zope/tal/tests/input/test02.xml
new file mode 100644
index 0000000..69567ea
--- /dev/null
+++ b/src/zope/tal/tests/input/test02.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" ?>
+<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
+
+<foo:header xmlns:foo="whomping-willow" plain="guido" quote='"' apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
+ <manifest>
+ <document>
+ <name>sample1</name>
+ <description>a simple invoice</description>
+ </document>
+ </manifest>
+</foo:header>
+
+<body>
+
+<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
+
+<Invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
+ <Header>
+ <InvoiceNumber>01786</InvoiceNumber>
+ <InvoiceDate>2000-03-17</InvoiceDate> <!-- March 17th, 2000 -->
+ <OrderNo>55377</OrderNo>
+ <OrderDate>2000-03-15</OrderDate> <!-- March 15th, 2000 -->
+ <CustomerPO>GJ03405</CustomerPO>
+ <ShipMethod>DAVE 1</ShipMethod>
+ <ShipDate>2000-03-17</ShipDate> <!-- March 17th, 2000 -->
+ <CustomerID>K5211(34)</CustomerID>
+ <SalesPersonCode>23</SalesPersonCode>
+ <TaxID>23</TaxID>
+ </Header>
+ <InvoiceTo>
+ <Name>SHIPWRIGHT RESTAURANTS LIMITED</Name>
+ <AddressLine>125 NORTH SERVICE ROAD W</AddressLine>
+ <AddressLine>WESTLAKE ACCESS</AddressLine>
+ <City>NORTH BAY</City>
+ <PostCode>L8B1O5</PostCode>
+ <State>ONTARIO</State>
+ <Country>CANADA</Country>
+ </InvoiceTo>
+ <ShipTo>
+ <Name/>
+ <AddressLine>ATTN: PAULINE DEGRASSI</AddressLine>
+ <City/>
+ <PostCode/>
+ <State/>
+ <Country/>
+ </ShipTo>
+ <DetailLines>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>CS</UnitOfMeasure>
+ <PartNumber>DM 5309</PartNumber>
+ <PartDescription>#1013 12 OZ.MUNICH STEIN</PartDescription>
+ <UnitPrice>37.72</UnitPrice>
+ <LineTotal>37.72</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>6</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6420</PartNumber>
+ <PartDescription>PROVINCIAL DINNER FORK</PartDescription>
+ <UnitPrice>17.98</UnitPrice>
+ <LineTotal>107.88</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>72</QuantityShipped>
+ <UnitOfMeasure>EA</UnitOfMeasure>
+ <PartNumber>JR20643</PartNumber>
+ <PartDescription>PLASTIC HANDLED STEAK KNIFE</PartDescription>
+ <UnitPrice>.81</UnitPrice>
+ <LineTotal>58.32</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>6</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6410</PartNumber>
+ <PartDescription>PROVINCIAL TEASPOONS</PartDescription>
+ <UnitPrice>12.16</UnitPrice>
+ <LineTotal>72.96</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>0</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6411</PartNumber>
+ <PartDescription>PROVINCIAL RD BOWL SPOON</PartDescription>
+ <QuantityBackOrdered>6</QuantityBackOrdered>
+ <UnitPrice>17.98</UnitPrice>
+ <LineTotal>0.00</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>EA</UnitOfMeasure>
+ <PartNumber>DO 3218</PartNumber>
+ <PartDescription>34 OZ DUAL DIAL SCALE AM3218</PartDescription>
+ <UnitPrice>70.00</UnitPrice>
+ <DiscountPercentage>5.0</DiscountPercentage>
+ <LineTotal>66.50</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>CS</UnitOfMeasure>
+ <PartNumber>DM 195</PartNumber>
+ <PartDescription>20 OZ.BEER PUB GLASS</PartDescription>
+ <UnitPrice>55.90</UnitPrice>
+ <LineTotal>55.90</LineTotal>
+ </DetailLine>
+ </DetailLines>
+ <Totals>
+ <SubTotal>399.28</SubTotal>
+ <DiscountTotal>3.50</DiscountTotal>
+ <FreightTotal>23.75</FreightTotal>
+ <GSTTotal>29.61</GSTTotal>
+ <ProvTaxTotal>33.84</ProvTaxTotal>
+ <OtherTotal>33.84</OtherTotal>
+ <InvoiceTotal>486.48</InvoiceTotal>
+ </Totals>
+</Invoice>
+
+</body>
+</biztalk_1>
diff --git a/src/zope/tal/tests/input/test03.html b/src/zope/tal/tests/input/test03.html
new file mode 100644
index 0000000..a0230e1
--- /dev/null
+++ b/src/zope/tal/tests/input/test03.html
@@ -0,0 +1,9 @@
+<p xmlns:z="http://xml.zope.org/namespaces/tal">
+ <span z:define="local x str:hello brave new world">
+ <span z:content="text local:x">outer variable x, first appearance</span>
+ <span z:define="local x str:goodbye cruel world">
+ <span z:content="text local:x">inner variable x</span>
+ </span>
+ <span z:content="text local:x">outer variable x, second appearance</span>
+ </span>
+</p>
diff --git a/src/zope/tal/tests/input/test03.xml b/src/zope/tal/tests/input/test03.xml
new file mode 100644
index 0000000..830149d
--- /dev/null
+++ b/src/zope/tal/tests/input/test03.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<p xmlns:z="http://xml.zope.org/namespaces/tal">
+ <span z:define="local x str:hello brave new world">
+ <span z:content="text local:x">outer variable x, first appearance</span>
+ <span z:define="local x str:goodbye cruel world">
+ <span z:content="text local:x">inner variable x</span>
+ </span>
+ <span z:content="text local:x">outer variable x, second appearance</span>
+ </span>
+</p>
diff --git a/src/zope/tal/tests/input/test04.html b/src/zope/tal/tests/input/test04.html
new file mode 100644
index 0000000..bdaad39
--- /dev/null
+++ b/src/zope/tal/tests/input/test04.html
@@ -0,0 +1,26 @@
+<html>
+
+ <body xmlns:m="http://xml.zope.org/namespaces/metal" xmlns:z="http://xml.zope.org/namespaces/tal" m:define-macro="body" z:define="global count python:0">
+
+ <ul m:define-macro="whoops">
+ <li z:repeat="item python:range(count)">
+ <span z:replace="item">1</span>
+ <span z:replace="global:message"/>
+ </li>
+ </ul>
+
+ <span z:define="global count python:2; global message str:hello world"/>
+
+ <p m:use-macro="whoops">use-macro
+ <span m:fill-slot="whoops">fill-slot</span>
+ </p>
+
+ <span z:define="global message str:goodbye cruel world"/>
+
+ <p m:use-macro="whoops">use-macro</p>
+
+ <p m:define-slot="whoops">define-slot</p>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/input/test04.xml b/src/zope/tal/tests/input/test04.xml
new file mode 100644
index 0000000..bde6cef
--- /dev/null
+++ b/src/zope/tal/tests/input/test04.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" ?>
+<html>
+
+ <body xmlns:m="http://xml.zope.org/namespaces/metal" xmlns:z="http://xml.zope.org/namespaces/tal" m:define-macro="body" z:define="global count python:0">
+
+ <ul m:define-macro="whoops">
+ <li z:repeat="item python:range(count)">
+ <span z:replace="item">1</span>
+ <span z:replace="global:message"/>
+ </li>
+ </ul>
+
+ <span z:define="global count python:2; global message str:hello world"/>
+
+ <p m:use-macro="whoops">use-macro
+ <span m:fill-slot="whoops">fill-slot</span>
+ </p>
+
+ <span z:define="global message str:goodbye cruel world"/>
+
+ <p m:use-macro="whoops">use-macro</p>
+
+ <p m:define-slot="whoops">define-slot</p>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/input/test05.html b/src/zope/tal/tests/input/test05.html
new file mode 100644
index 0000000..21f6b68
--- /dev/null
+++ b/src/zope/tal/tests/input/test05.html
@@ -0,0 +1,9 @@
+<html>
+
+ <body xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="body">
+
+ <h1>This is the body of test5</h1>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/input/test05.xml b/src/zope/tal/tests/input/test05.xml
new file mode 100644
index 0000000..fcaaf6b
--- /dev/null
+++ b/src/zope/tal/tests/input/test05.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<html>
+
+ <body xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="body">
+
+ <h1>This is the body of test5</h1>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/input/test06.html b/src/zope/tal/tests/input/test06.html
new file mode 100644
index 0000000..ac1264d
--- /dev/null
+++ b/src/zope/tal/tests/input/test06.html
@@ -0,0 +1,6 @@
+<html>
+ <body xmlns:m="http://xml.zope.org/namespaces/metal"
+ m:use-macro="tests/input/test05.html/body">
+ dummy body in test6
+ </body>
+</html>
diff --git a/src/zope/tal/tests/input/test06.xml b/src/zope/tal/tests/input/test06.xml
new file mode 100644
index 0000000..b32bd0f
--- /dev/null
+++ b/src/zope/tal/tests/input/test06.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" ?>
+<html>
+ <body xmlns:m="http://xml.zope.org/namespaces/metal"
+ m:use-macro="tests/input/test05.xml/body">
+ dummy body in test6
+ </body>
+</html>
diff --git a/src/zope/tal/tests/input/test07.html b/src/zope/tal/tests/input/test07.html
new file mode 100644
index 0000000..bff98f0
--- /dev/null
+++ b/src/zope/tal/tests/input/test07.html
@@ -0,0 +1,11 @@
+<table xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="myTable">
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span m:define-slot="bottomRight">Bottom Right</span></td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/input/test07.xml b/src/zope/tal/tests/input/test07.xml
new file mode 100644
index 0000000..e5c520a
--- /dev/null
+++ b/src/zope/tal/tests/input/test07.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<table xmlns:m="http://xml.zope.org/namespaces/metal" m:define-macro="myTable">
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span m:define-slot="bottomRight">Bottom Right</span></td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/input/test08.html b/src/zope/tal/tests/input/test08.html
new file mode 100644
index 0000000..1e4915b
--- /dev/null
+++ b/src/zope/tal/tests/input/test08.html
@@ -0,0 +1,44 @@
+<table xmlns:m="http://xml.zope.org/namespaces/metal" m:use-macro="tests/input/test07.html/myTable">
+<!-- macro use with slots -->
+ <tr>
+ <td>
+ <span m:fill-slot="bottomRight">
+ <h1>Some headline</h1>
+ <p>This is the real contents of the bottom right slot.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ </span>
+ </td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/input/test08.xml b/src/zope/tal/tests/input/test08.xml
new file mode 100644
index 0000000..b0360fa
--- /dev/null
+++ b/src/zope/tal/tests/input/test08.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" ?>
+<table xmlns:m="http://xml.zope.org/namespaces/metal" m:use-macro="tests/input/test07.xml/myTable">
+<!-- macro use with slots -->
+ <tr>
+ <td>
+ <span m:fill-slot="bottomRight">
+ <h1>Some headline</h1>
+ <p>This is the real contents of the bottom right slot.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ </span>
+ </td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/input/test09.html b/src/zope/tal/tests/input/test09.html
new file mode 100644
index 0000000..35f481a
--- /dev/null
+++ b/src/zope/tal/tests/input/test09.html
@@ -0,0 +1,30 @@
+<html>
+<body>
+<p>
+ Just a bunch of text.
+<p>more text...
+<ul>
+ <li>first item
+ <li>second item
+
+ <ol>
+ <li>second list, first item
+ <li>second list, second item
+ <dl compact>
+ <dt>term 1
+ <dt>term 2
+ <dd>definition
+ </dl>
+ </ol>
+
+ <li>Now let's have a paragraph...
+ <p>My Paragraph
+ </li>
+
+ <li>And a table in a list item:
+ <table>
+ </table>
+</ul>
+
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test09.xml b/src/zope/tal/tests/input/test09.xml
new file mode 100644
index 0000000..c3d10d7
--- /dev/null
+++ b/src/zope/tal/tests/input/test09.xml
@@ -0,0 +1,30 @@
+<html>
+<body>
+<p>
+ Just a bunch of text.</p>
+<p>more text...</p>
+<ul>
+ <li>first item</li>
+ <li>second item
+
+ <ol>
+ <li>second list, first item</li>
+ <li>second list, second item
+ <dl compact="">
+ <dt>term 1</dt>
+ <dt>term 2</dt>
+ <dd>definition</dd>
+ </dl></li>
+ </ol></li>
+
+ <li>Now let's have a paragraph...
+ <p>My Paragraph</p>
+ </li>
+
+ <li>And a table in a list item:
+ <table>
+ </table></li>
+</ul>
+
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test10.html b/src/zope/tal/tests/input/test10.html
new file mode 100644
index 0000000..6ecca4c
--- /dev/null
+++ b/src/zope/tal/tests/input/test10.html
@@ -0,0 +1,48 @@
+<html><body>
+<table xmlns:m="http://xml.zope.org/namespaces/metal" m:use-macro="tests/input/test07.html/myTable">
+<!-- macro use with slots -->
+ <tr>
+ <td>
+ <span m:fill-slot="bottomRight">
+ <h1>Some headline</h1>
+ <p>This is the real contents of the bottom right slot.</p>
+ <hr>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <br><br>
+ </span>
+ </td>
+ </tr>
+</table>
+</body></html>
diff --git a/src/zope/tal/tests/input/test11.html b/src/zope/tal/tests/input/test11.html
new file mode 100644
index 0000000..89f7563
--- /dev/null
+++ b/src/zope/tal/tests/input/test11.html
@@ -0,0 +1,19 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <p tal:replace="structure string:&lt;a&gt;bar&lt;/a&gt;"
+ tal:attributes="href string:http://www.python.org">dummy text</p>
+ <p tal:define="x python:1" tal:on-error="string:bad boy!">
+ <span tal:define="x python:2">
+ <span tal:define="x python:3">
+ <span tal:content="python:1/0"/>
+ </span>
+ </span>
+ </p>
+ <p tal:on-error="string:x undefined">
+ <span tal:content="x"/>
+ </p>
+ <tal:block on-error="string:x undefined" replace="x" />
+ <tal:block on-error="string:x undefined">
+ <p tal:content="x">p</p>
+ </tal:block>
+ <div tal:replace="structure string:&lt;hr /&gt;">rule</div>
+</html>
diff --git a/src/zope/tal/tests/input/test11.xml b/src/zope/tal/tests/input/test11.xml
new file mode 100644
index 0000000..435f95c
--- /dev/null
+++ b/src/zope/tal/tests/input/test11.xml
@@ -0,0 +1,14 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <p tal:replace="structure string:&lt;a&gt;bar&lt;/a&gt;"
+ tal:attributes="href string:http://www.python.org">dummy text</p>
+ <p tal:define="x python:1" tal:on-error="string:bad boy!">
+ <span tal:define="x python:2">
+ <span tal:define="x python:3">
+ <span tal:content="python:1/0"/>
+ </span>
+ </span>
+ </p>
+ <p tal:on-error="string:x undefined">
+ <span tal:content="x"/>
+ </p>
+</html>
diff --git a/src/zope/tal/tests/input/test12.html b/src/zope/tal/tests/input/test12.html
new file mode 100644
index 0000000..94d9a66
--- /dev/null
+++ b/src/zope/tal/tests/input/test12.html
@@ -0,0 +1,24 @@
+<span tal:define="global true python:1; global false python:0" />
+
+<img ismap>
+<img ismap=ismap>
+<img ismap="ismap">
+<img ismap="foo">
+
+<img ismap tal:attributes="ismap true">
+<img ismap tal:attributes="ismap false">
+<img ismap tal:attributes="ismap nothing">
+
+<img ismap="foo" tal:attributes="ismap true">
+<img ismap="foo" tal:attributes="ismap false">
+<img ismap="foo" tal:attributes="ismap nothing">
+
+<img tal:attributes="ismap true">
+<img tal:attributes="ismap false">
+<img tal:attributes="ismap nothing">
+
+<span tal:define="global x string:x.gif" />
+
+<img src="foo">
+<img src="foo" tal:attributes="src x">
+<img src="foo" tal:attributes="src nothing">
diff --git a/src/zope/tal/tests/input/test13.html b/src/zope/tal/tests/input/test13.html
new file mode 100644
index 0000000..d68e0ce
--- /dev/null
+++ b/src/zope/tal/tests/input/test13.html
@@ -0,0 +1,7 @@
+Here's a stray greater than: >
+
+<script>
+ <!-- no comment -->
+ <notag>
+ &noentity;
+</script>
diff --git a/src/zope/tal/tests/input/test14.html b/src/zope/tal/tests/input/test14.html
new file mode 100644
index 0000000..0aaa751
--- /dev/null
+++ b/src/zope/tal/tests/input/test14.html
@@ -0,0 +1,10 @@
+<table>
+ <tr>
+ <td tal:repeat="x python:['car', 'bike', 'broomstick']" tal:content="x">
+ </td>
+ </tr>
+</table>
+
+<p>
+ <span tal:repeat="x python:['Harry', 'Ron', 'Hermione']" tal:replace="x" />
+</p>
diff --git a/src/zope/tal/tests/input/test14.xml b/src/zope/tal/tests/input/test14.xml
new file mode 100644
index 0000000..c596135
--- /dev/null
+++ b/src/zope/tal/tests/input/test14.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" ?>
+<html xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<table>
+ <tr>
+ <td tal:repeat="x python:['car', 'bike', 'broomstick']" tal:content="x">
+ </td>
+ </tr>
+</table>
+
+<p>
+ <span tal:repeat="x python:['Harry', 'Ron', 'Hermione']" tal:replace="x" />
+</p>
+
+</html>
diff --git a/src/zope/tal/tests/input/test15.html b/src/zope/tal/tests/input/test15.html
new file mode 100644
index 0000000..0cd456e
--- /dev/null
+++ b/src/zope/tal/tests/input/test15.html
@@ -0,0 +1,26 @@
+<span metal:define-macro="INNER">
+ <span metal:define-slot="INNERSLOT">INNERSLOT</span>
+</span>
+
+<xxx metal:use-macro="INNER">
+ <xxx metal:fill-slot="INNERSLOT">inner-argument</xxx>
+</xxx>
+
+<div metal:define-macro="OUTER">
+<div metal:use-macro="INNER">
+ <xxx metal:define-slot="OUTERSLOT" metal:fill-slot="INNERSLOT">
+ OUTERSLOT
+ </xxx>
+</div>
+</div>
+
+<div metal:use-macro="OUTER">
+<span>
+ <xxx>
+ <div metal:fill-slot="OUTERSLOT">outer-argument</div>
+ </xxx>
+</span>
+</div>
+
+<div metal:use-macro="OUTER">
+</div>
diff --git a/src/zope/tal/tests/input/test16.html b/src/zope/tal/tests/input/test16.html
new file mode 100644
index 0000000..1414f45
--- /dev/null
+++ b/src/zope/tal/tests/input/test16.html
@@ -0,0 +1,2 @@
+<a href="valid/link.html"
+ tal:attributes="href python:'/base/' + attrs['href']">blah, blah</a>
diff --git a/src/zope/tal/tests/input/test16.xml b/src/zope/tal/tests/input/test16.xml
new file mode 100644
index 0000000..2efb2ab
--- /dev/null
+++ b/src/zope/tal/tests/input/test16.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<body xmlns:tal="http://xml.zope.org/namespaces/tal">
+
+<ImG href="foo" Alt="bar"
+ tal:attributes="Href string:about:foo;alT string:baz" />
+
+</body>
diff --git a/src/zope/tal/tests/input/test17.html b/src/zope/tal/tests/input/test17.html
new file mode 100644
index 0000000..5a5ebb3
--- /dev/null
+++ b/src/zope/tal/tests/input/test17.html
@@ -0,0 +1,6 @@
+<tal:block tal:content="string:Yes">No</tal:block>
+<tal:block content="string:Yes">No</tal:block>
+<tal:block>Yes</tal:block>
+
+<metal:block tal:content="string:Yes">No</metal:block>
+<metal:block>Yes</metal:block>
diff --git a/src/zope/tal/tests/input/test17.xml b/src/zope/tal/tests/input/test17.xml
new file mode 100644
index 0000000..ecb617a
--- /dev/null
+++ b/src/zope/tal/tests/input/test17.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<body xmlns:z="http://xml.zope.org/namespaces/tal"
+ xmlns:z2="http://xml.zope.org/namespaces/metal">
+<z:block z:content="string:Yes">No</z:block>
+<z:block content="string:Yes">No</z:block>
+<z:block>Yes</z:block>
+
+<z2:block z:content="string:Yes">No</z2:block>
+<z2:block>Yes</z2:block>
+</body>
diff --git a/src/zope/tal/tests/input/test18.html b/src/zope/tal/tests/input/test18.html
new file mode 100644
index 0000000..c3a5c26
--- /dev/null
+++ b/src/zope/tal/tests/input/test18.html
@@ -0,0 +1,16 @@
+<p tal:omit-tag="">Content</p>
+<p tal:omit-tag=""></p>
+<img tal:omit-tag="">
+
+<p tal:omit-tag="string:Yes">Content</p>
+<p tal:omit-tag="string:Yes"></p>
+<img tal:omit-tag="string:Yes">
+
+<p tal:omit-tag="nothing">Content</p>
+<p tal:omit-tag="nothing"></p>
+<img tal:omit-tag="nothing">
+
+<p tal:define="txt string:Yes" tal:omit-tag="" tal:content="txt">No</p>
+<p tal:define="txt string:Yes" tal:omit-tag="" tal:replace="txt">No</p>
+<p tal:omit-tag="" tal:content="default">Yes</p>
+<p tal:omit-tag="" tal:replace="default">Yes</p>
diff --git a/src/zope/tal/tests/input/test18.xml b/src/zope/tal/tests/input/test18.xml
new file mode 100644
index 0000000..5a0cca4
--- /dev/null
+++ b/src/zope/tal/tests/input/test18.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<body xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal">
+<p tal:omit-tag="">Content</p>
+<p tal:omit-tag=""></p>
+<img tal:omit-tag=""/>
+
+<p tal:omit-tag="string:Yes">Content</p>
+<p tal:omit-tag="string:Yes"></p>
+<img tal:omit-tag="string:Yes"/>
+
+<p tal:omit-tag="nothing">Content</p>
+<p tal:omit-tag="nothing"></p>
+<img tal:omit-tag="nothing" />
+
+<p tal:define="txt string:Yes" tal:omit-tag="" tal:content="txt">No</p>
+<p tal:define="txt string:Yes" tal:omit-tag="" tal:replace="txt">No</p>
+<p tal:omit-tag="" tal:content="default">Yes</p>
+<p tal:omit-tag="" tal:replace="default">Yes</p>
+</body>
diff --git a/src/zope/tal/tests/input/test19.html b/src/zope/tal/tests/input/test19.html
new file mode 100644
index 0000000..a56632a
--- /dev/null
+++ b/src/zope/tal/tests/input/test19.html
@@ -0,0 +1,5 @@
+<span i18n:translate="">Replace this</span>
+<span i18n:translate="msgid">This is a
+translated string</span>
+<span i18n:translate="">And another
+translated string</span>
diff --git a/src/zope/tal/tests/input/test19.xml b/src/zope/tal/tests/input/test19.xml
new file mode 100644
index 0000000..fe4bf79
--- /dev/null
+++ b/src/zope/tal/tests/input/test19.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<body xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+<span i18n:translate="">Replace this</span>
+<span i18n:translate="msgid">This is a
+translated string</span>
+<span i18n:translate="">And another
+translated string</span>
+</body>
diff --git a/src/zope/tal/tests/input/test20.html b/src/zope/tal/tests/input/test20.html
new file mode 100644
index 0000000..f302213
--- /dev/null
+++ b/src/zope/tal/tests/input/test20.html
@@ -0,0 +1 @@
+<span i18n:translate="">replaceable <p tal:replace="str:here">content</p></span>
diff --git a/src/zope/tal/tests/input/test20.xml b/src/zope/tal/tests/input/test20.xml
new file mode 100644
index 0000000..5050883
--- /dev/null
+++ b/src/zope/tal/tests/input/test20.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<body xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+<span i18n:translate="">replaceable <p tal:replace="str:here">content</p></span>
+</body>
diff --git a/src/zope/tal/tests/input/test21.html b/src/zope/tal/tests/input/test21.html
new file mode 100644
index 0000000..95f925e
--- /dev/null
+++ b/src/zope/tal/tests/input/test21.html
@@ -0,0 +1,4 @@
+<span i18n:translate="">
+ <span tal:replace="str:Lomax" i18n:name="name" /> was born in
+ <span tal:replace="str:Antarctica" i18n:name="country" />.
+</span>
diff --git a/src/zope/tal/tests/input/test21.xml b/src/zope/tal/tests/input/test21.xml
new file mode 100644
index 0000000..eea370b
--- /dev/null
+++ b/src/zope/tal/tests/input/test21.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<body xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n">
+<span i18n:translate="">
+ <span tal:replace="str:Lomax" i18n:name="name" /> was born in
+ <span tal:replace="str:Antarctica" i18n:name="country" />.
+</span>
+</body>
diff --git a/src/zope/tal/tests/input/test22.html b/src/zope/tal/tests/input/test22.html
new file mode 100644
index 0000000..a4a7e93
--- /dev/null
+++ b/src/zope/tal/tests/input/test22.html
@@ -0,0 +1,4 @@
+<span i18n:translate="">
+ <span tal:omit-tag="" i18n:name="name"><b>Jim</b></span> was born in
+ <span tal:omit-tag="" i18n:name="country">the USA</span>.
+</span>
diff --git a/src/zope/tal/tests/input/test22.xml b/src/zope/tal/tests/input/test22.xml
new file mode 100644
index 0000000..54b57d8
--- /dev/null
+++ b/src/zope/tal/tests/input/test22.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<body xmlns:tal="http://xml.zope.org/namespaces/tal">
+ <span tal:content="default">content</span>
+ <span tal:omit-tag="" tal:content="default">omit</span>
+ <span tal:replace="default">replace</span>
+</body>
diff --git a/src/zope/tal/tests/input/test23.html b/src/zope/tal/tests/input/test23.html
new file mode 100644
index 0000000..bfe6665
--- /dev/null
+++ b/src/zope/tal/tests/input/test23.html
@@ -0,0 +1,2 @@
+<span i18n:data="here/currentTime"
+ i18n:translate="timefmt">2:32 pm</span>
diff --git a/src/zope/tal/tests/input/test24.html b/src/zope/tal/tests/input/test24.html
new file mode 100644
index 0000000..6d53984
--- /dev/null
+++ b/src/zope/tal/tests/input/test24.html
@@ -0,0 +1,12 @@
+<input name="Delete"
+ tal:attributes="name string:delete_button"
+ i18n:attributes="name">
+
+<input name="Delete"
+ i18n:attributes="name message-id">
+
+<input i18n:attributes=" name message-id;
+ attr input-attr ">
+
+<input i18n:attributes=" name message-id;
+ attr input-attr;">
diff --git a/src/zope/tal/tests/input/test25.html b/src/zope/tal/tests/input/test25.html
new file mode 100644
index 0000000..25a99cf
--- /dev/null
+++ b/src/zope/tal/tests/input/test25.html
@@ -0,0 +1 @@
+<input name="Delete" i18n:attributes="name">
diff --git a/src/zope/tal/tests/input/test26.html b/src/zope/tal/tests/input/test26.html
new file mode 100644
index 0000000..fa5a99d
--- /dev/null
+++ b/src/zope/tal/tests/input/test26.html
@@ -0,0 +1,3 @@
+<span i18n:translate="jobnum">
+ Job #<span tal:replace="context/@@object_name"
+ i18n:name="jobnum">NN</span></span>
diff --git a/src/zope/tal/tests/input/test27.html b/src/zope/tal/tests/input/test27.html
new file mode 100644
index 0000000..b9c16cb
--- /dev/null
+++ b/src/zope/tal/tests/input/test27.html
@@ -0,0 +1,5 @@
+<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>
diff --git a/src/zope/tal/tests/input/test28.html b/src/zope/tal/tests/input/test28.html
new file mode 100644
index 0000000..0364663
--- /dev/null
+++ b/src/zope/tal/tests/input/test28.html
@@ -0,0 +1,5 @@
+<p i18n:translate="verify">Your contact email address is recorded as
+ <span tal:omit-tag="" i18n:name="email">
+ <a href="mailto:user@example.com"
+ tal:content="request/submitter">user@host.com</a></span>
+</p>
diff --git a/src/zope/tal/tests/input/test29.html b/src/zope/tal/tests/input/test29.html
new file mode 100644
index 0000000..e2f1e82
--- /dev/null
+++ b/src/zope/tal/tests/input/test29.html
@@ -0,0 +1,4 @@
+<div i18n:translate="">At the tone the time will be
+<span i18n:data="here/currentTime"
+ i18n:translate="timefmt"
+ i18n:name="time">2:32 pm</span>... beep!</div>
diff --git a/src/zope/tal/tests/input/test30.html b/src/zope/tal/tests/input/test30.html
new file mode 100644
index 0000000..6f8c6ef
--- /dev/null
+++ b/src/zope/tal/tests/input/test30.html
@@ -0,0 +1,6 @@
+<p i18n:translate="verify">Your contact email address is recorded as
+<a href="user@host.com"
+ tal:attributes="href string:mailto:${request/submitter}"
+ tal:content="request/submitter"
+ i18n:name="email">user@host.com</a>
+</p>
diff --git a/src/zope/tal/tests/input/test31.html b/src/zope/tal/tests/input/test31.html
new file mode 100644
index 0000000..c927f42
--- /dev/null
+++ b/src/zope/tal/tests/input/test31.html
@@ -0,0 +1,7 @@
+<p i18n:translate="verify">Your contact email address is recorded as
+<span tal:omit-tag="" i18n:name="email">
+<a href="user@host.com"
+ tal:attributes="href string:mailto:${request/submitter}"
+ tal:content="request/submitter">
+ user@host.com</a></span>
+</p>
diff --git a/src/zope/tal/tests/input/test32.html b/src/zope/tal/tests/input/test32.html
new file mode 100644
index 0000000..3b09bad
--- /dev/null
+++ b/src/zope/tal/tests/input/test32.html
@@ -0,0 +1,4 @@
+<span i18n:translate="origin">
+ <span tal:content="str:Lomax" i18n:name="name" /> was born in
+ <span tal:content="str:Antarctica" i18n:name="country" />.
+</span>
diff --git a/src/zope/tal/tests/input/test33.html b/src/zope/tal/tests/input/test33.html
new file mode 100644
index 0000000..f5dcf58
--- /dev/null
+++ b/src/zope/tal/tests/input/test33.html
@@ -0,0 +1 @@
+<span i18n:translate="">don't translate me</span>
diff --git a/src/zope/tal/tests/input/test34.html b/src/zope/tal/tests/input/test34.html
new file mode 100644
index 0000000..4cd6ff0
--- /dev/null
+++ b/src/zope/tal/tests/input/test34.html
@@ -0,0 +1,11 @@
+<span i18n:translate="don't translate me">
+ stuff
+ <span tal:replace="string:foobar" i18n:name="longname" />
+ more stuff
+</span>
+
+<span i18n:translate="">
+ stuff
+ <span tal:replace="string:foobar" i18n:name="longname" />
+ more stuff
+</span>
diff --git a/src/zope/tal/tests/input/test35.html b/src/zope/tal/tests/input/test35.html
new file mode 100644
index 0000000..7964e9f
--- /dev/null
+++ b/src/zope/tal/tests/input/test35.html
@@ -0,0 +1,7 @@
+<span metal:define-macro="page" tal:omit-tag="">
+ <h1 metal:define-slot="name" tal:omit-tag="" />
+</span>
+
+<span metal:use-macro="page">
+ <h1 metal:fill-slot="name" tal:content="macroname">name</h1>
+</span> \ No newline at end of file
diff --git a/src/zope/tal/tests/input/test36.html b/src/zope/tal/tests/input/test36.html
new file mode 100644
index 0000000..bf4932a
--- /dev/null
+++ b/src/zope/tal/tests/input/test36.html
@@ -0,0 +1,6 @@
+<span tal:replace="string:<foo>" />
+<span i18n:translate="">
+ <span tal:replace="string:<foo>" i18n:name="name1" />
+ <span tal:replace="structure string:<bar />" i18n:name="name2" />
+ <span tal:omit-tag="" i18n:name="name3"><b>some</b> <i>text</i></span>
+</span>
diff --git a/src/zope/tal/tests/input/test_domain.html b/src/zope/tal/tests/input/test_domain.html
new file mode 100644
index 0000000..95d40a2
--- /dev/null
+++ b/src/zope/tal/tests/input/test_domain.html
@@ -0,0 +1,7 @@
+<div i18n:domain="lower">
+<span i18n:translate="">Replace this</span>
+<span i18n:translate="msgid">This is a
+translated string</span>
+<span i18n:translate="">And another
+translated string</span>
+</div>
diff --git a/src/zope/tal/tests/input/test_failed_attr_translation.html b/src/zope/tal/tests/input/test_failed_attr_translation.html
new file mode 100644
index 0000000..1c395c7
--- /dev/null
+++ b/src/zope/tal/tests/input/test_failed_attr_translation.html
@@ -0,0 +1,2 @@
+<input value="don't translate me"
+ i18n:attributes="value">
diff --git a/src/zope/tal/tests/input/test_metal1.html b/src/zope/tal/tests/input/test_metal1.html
new file mode 100644
index 0000000..a5371ce
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal1.html
@@ -0,0 +1,61 @@
+<span metal:define-macro="OUTER">
+ AAA
+ <span metal:define-macro="INNER">INNER</span>
+ BBB
+</span>
+
+<xxx metal:use-macro="OUTER">
+</xxx>
+
+<xxx metal:use-macro="INNER">
+</xxx>
+
+<span metal:define-macro="OUTER2">
+ AAA
+ <xxx metal:define-slot="OUTERSLOT">
+ <span metal:define-macro="INNER2">INNER</span>
+ </xxx>
+ BBB
+</span>
+
+<xxx metal:use-macro="OUTER2">
+</xxx>
+
+<xxx metal:use-macro="INNER2">
+</xxx>
+
+<xxx metal:use-macro="OUTER2">
+ <yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
+</xxx>
+
+<span metal:define-macro="OUTER3">
+ AAA
+ <xxx metal:define-slot="OUTERSLOT">
+ <span metal:define-macro="INNER3">INNER
+ <xxx metal:define-slot="INNERSLOT">INNERSLOT</xxx>
+ </span>
+ </xxx>
+ BBB
+</span>
+
+<xxx metal:use-macro="OUTER3">
+</xxx>
+
+<xxx metal:use-macro="OUTER3">
+ <yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
+</xxx>
+
+<xxx metal:use-macro="INNER3">
+</xxx>
+
+<xxx metal:use-macro="INNER3">
+ <yyy metal:fill-slot="INNERSLOT">INNERSLOT</yyy>
+</xxx>
+
+<xxx metal:use-macro="INNER3">
+ <yyy metal:fill-slot="INNERSLOT">
+ <zzz metal:define-macro="INSLOT">INSLOT</zzz>
+ </yyy>
+</xxx>
+
+<xxx metal:use-macro="INSLOT"></xxx>
diff --git a/src/zope/tal/tests/input/test_metal2.html b/src/zope/tal/tests/input/test_metal2.html
new file mode 100644
index 0000000..425508a
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal2.html
@@ -0,0 +1,7 @@
+<div metal:define-macro="OUTER">
+ OUTER
+ <span metal:define-macro="INNER">INNER</span>
+ OUTER
+</div>
+
+<div metal:use-macro="OUTER"/>
diff --git a/src/zope/tal/tests/input/test_metal3.html b/src/zope/tal/tests/input/test_metal3.html
new file mode 100644
index 0000000..b0af907
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal3.html
@@ -0,0 +1 @@
+<span tal:attributes="class string:foo">Should not get attr in metal</span>
diff --git a/src/zope/tal/tests/input/test_metal4.html b/src/zope/tal/tests/input/test_metal4.html
new file mode 100644
index 0000000..dc774d3
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal4.html
@@ -0,0 +1,4 @@
+<!-- the outer element *must* be tal:something or metal:something -->
+<metal:block define-macro="page" i18n:domain="zope">
+ <title metal:define-slot="title">Z3 UI</title>
+</metal:block>
diff --git a/src/zope/tal/tests/input/test_metal5.html b/src/zope/tal/tests/input/test_metal5.html
new file mode 100644
index 0000000..8bae3d8
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal5.html
@@ -0,0 +1,4 @@
+<!-- the outer element *must* include tal:omit-tag='' -->
+<x tal:omit-tag="" metal:define-macro="page" i18n:domain="zope">
+ <title metal:define-slot="title">Z3 UI</title>
+</x>
diff --git a/src/zope/tal/tests/input/test_metal6.html b/src/zope/tal/tests/input/test_metal6.html
new file mode 100644
index 0000000..ce243f2
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal6.html
@@ -0,0 +1,5 @@
+<metal:block define-macro="page">
+ <html i18:domain="zope">
+ <metal:block define-slot="title">Z3 UI</metal:block>
+ </html>
+</metal:block>
diff --git a/src/zope/tal/tests/input/test_metal7.html b/src/zope/tal/tests/input/test_metal7.html
new file mode 100644
index 0000000..75ec511
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal7.html
@@ -0,0 +1,6 @@
+<html metal:define-macro="page" i18n:domain="zope">
+ <x metal:define-slot="title" />
+</html>
+<html metal:use-macro="page">
+ <x metal:fill-slot="title" />
+</html>
diff --git a/src/zope/tal/tests/input/test_metal8.html b/src/zope/tal/tests/input/test_metal8.html
new file mode 100644
index 0000000..40d8a43
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal8.html
@@ -0,0 +1,15 @@
+<html metal:define-macro="page" i18n:domain="zope">
+<body>
+<div metal:define-macro="workspace">
+<div metal:define-slot="body">
+Default body
+</div>
+</div>
+</body>
+</html>
+
+<html metal:use-macro="page">
+<div metal:fill-slot="body">
+Filled-in body
+</div>
+</html>
diff --git a/src/zope/tal/tests/input/test_metal9.html b/src/zope/tal/tests/input/test_metal9.html
new file mode 100644
index 0000000..46b1b45
--- /dev/null
+++ b/src/zope/tal/tests/input/test_metal9.html
@@ -0,0 +1,23 @@
+<div metal:define-macro="macro1" i18n:domain="zope">
+<span metal:define-slot="slot1">
+Default for macro1
+</span>
+</div>
+
+<div metal:define-macro="macro2" metal:extend-macro="macro1" i18n:domain="zope">
+<span metal:fill-slot="slot1">
+Macro 2's slot 1 decoration
+<span metal:define-slot="slot1">
+Default for macro2
+</span>
+</span>
+</div>
+
+<div metal:use-macro="macro2">
+</div>
+
+<div metal:use-macro="macro2">
+<span metal:fill-slot="slot1">
+Custom slot1
+</span>
+</div>
diff --git a/src/zope/tal/tests/input/test_sa1.html b/src/zope/tal/tests/input/test_sa1.html
new file mode 100644
index 0000000..8879865
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa1.html
@@ -0,0 +1,6 @@
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test_sa1.xml b/src/zope/tal/tests/input/test_sa1.xml
new file mode 100644
index 0000000..d00a46d
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa1.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" ?>
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test_sa2.html b/src/zope/tal/tests/input/test_sa2.html
new file mode 100644
index 0000000..1c4e06b
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test_sa2.xml b/src/zope/tal/tests/input/test_sa2.xml
new file mode 100644
index 0000000..b54d6a1
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test_sa3.html b/src/zope/tal/tests/input/test_sa3.html
new file mode 100644
index 0000000..675805d
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa3.html
@@ -0,0 +1,15 @@
+<html>
+<body>
+ <div metal:define-macro="macro1">This is macro1 on sa3 line 3.
+ <span metal:define-slot="slot1">This is slot1 on sa3 line 4.</span>
+ This is the end of macro1 on sa3 line 5.
+ </div>
+ <p>Some text on sa3 line 7.</p>
+ <p metal:use-macro="macro1">
+ This text on sa3 line 9 will disappear.
+ <b metal:fill-slot="slot1">Text from sa3 line 10 is filled into slot1.</b>
+ This text on sa3 line 11 will disappear.
+ </p>
+ <p>This is some text on sa3 line 13.</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test_sa3.xml b/src/zope/tal/tests/input/test_sa3.xml
new file mode 100644
index 0000000..79e3251
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa3.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<html>
+<body xmlns:metal="http://xml.zope.org/namespaces/metal">
+ <div metal:define-macro="macro1">This is macro1 on sa3 line 4.
+ <span metal:define-slot="slot1">This is slot1 on sa3 line 5.</span>
+ This is the end of macro1 on sa3 line 6.
+ </div>
+ <p>Some text on sa3 line 8.</p>
+ <p metal:use-macro="macro1">
+ This text on sa3 line 10 will disappear.
+ <b metal:fill-slot="slot1">Text from sa3 line 11 is filled into slot1.</b>
+ This text on sa3 line 12 will disappear.
+ </p>
+ <p>This is some text on sa3 line 14.</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/input/test_sa4.html b/src/zope/tal/tests/input/test_sa4.html
new file mode 100644
index 0000000..97596f6
--- /dev/null
+++ b/src/zope/tal/tests/input/test_sa4.html
@@ -0,0 +1,11 @@
+<html>
+<body>
+ <p>Some text on sa4 line 3.</p>
+ <p metal:use-macro="tests/input/test_sa3.html/macro1">
+ This text on sa4 line 5 will disappear.
+ <b metal:fill-slot="slot1">Text from sa4 line 6 is filled into slot1.</b>
+ This text on sa4 line 7 will disappear.
+ </p>
+ <p>This is some text on sa4 line 9.</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/markbench.py b/src/zope/tal/tests/markbench.py
new file mode 100644
index 0000000..f08f9e2
--- /dev/null
+++ b/src/zope/tal/tests/markbench.py
@@ -0,0 +1,187 @@
+#! /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.1 (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
+
+$Id$
+"""
+
+import warnings
+warnings.filterwarnings("ignore", category=DeprecationWarning)
+
+import os
+os.environ['NO_SECURITY'] = 'true'
+
+import getopt
+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)
+
+# Figure out where the benchmark files are:
+try:
+ fname = __file__
+except NameError:
+ fname = sys.argv[0]
+taldir = os.path.dirname(os.path.dirname(os.path.abspath(fname)))
+benchdir = os.path.join(taldir, 'benchmark')
+
+# Construct templates for the filenames:
+tal_fn = os.path.join(benchdir, 'tal%.2d.html')
+dtml_fn = os.path.join(benchdir, 'dtml%.2d.html')
+
+def compare(n, count, profiler=None, verbose=1):
+ if verbose:
+ 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, verbose=1):
+ n = 1
+ if verbose:
+ 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, verbose)
+ n = n + 1
+
+def get_signal_name(sig):
+ import signal
+ for name in dir(signal):
+ if getattr(signal, name) == sig:
+ return name
+ return None
+
+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
+ runtests = False
+ verbose = True
+
+ opts, args = getopt.getopt(sys.argv[1:], "pqt")
+ for opt, arg in opts:
+ if opt == "-p":
+ import profile
+ profiler = profile.Profile()
+ elif opt == "-q":
+ verbose = False
+ elif opt == "-t":
+ runtests = True
+
+ if runtests:
+ srcdir = os.path.dirname(os.path.dirname(taldir))
+ topdir = os.path.dirname(srcdir)
+ pwd = os.getcwd()
+ os.chdir(topdir)
+ rc = os.spawnl(os.P_WAIT, sys.executable,
+ sys.executable, "test.py", "zope.tal.tests")
+ if rc > 0:
+ # TODO: Failing tests don't cause test.py to report an
+ # error; not sure why. ;-(
+ sys.exit(rc)
+ elif rc < 0:
+ sig = -rc
+ print >>sys.stderr, (
+ "Process exited, signal %d (%s)."
+ % (sig, get_signal_name(sig) or "<unknown signal>"))
+ sys.exit(1)
+ os.chdir(pwd)
+
+ if len(args) >= 1:
+ for arg in args:
+ compare(int(arg), 25, profiler, verbose)
+ else:
+ main(25, profiler, verbose)
+
+ 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
diff --git a/src/zope/tal/tests/output/__init__.py b/src/zope/tal/tests/output/__init__.py
new file mode 100644
index 0000000..b711d36
--- /dev/null
+++ b/src/zope/tal/tests/output/__init__.py
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
diff --git a/src/zope/tal/tests/output/acme_template.html b/src/zope/tal/tests/output/acme_template.html
new file mode 100644
index 0000000..3d37355
--- /dev/null
+++ b/src/zope/tal/tests/output/acme_template.html
@@ -0,0 +1,26 @@
+<!-- This is ACME's generic look and feel, which is based on
+PNOME's look and feel. -->
+<html>
+<head>
+<title>ACME Look and Feel</title>
+
+
+</head>
+<body>
+<div>
+ <div>
+ "The early bird gets the worm, but the second mouse gets the cheese."
+ </div>
+ <a href="#">Preferences...</a>
+</div>
+<div>
+ Content here
+</div>
+<div>
+Copyright 2004 Acme Inc.
+<div>
+Standard disclaimers apply.
+</div>
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/acme_template_source.html b/src/zope/tal/tests/output/acme_template_source.html
new file mode 100644
index 0000000..11f19d3
--- /dev/null
+++ b/src/zope/tal/tests/output/acme_template_source.html
@@ -0,0 +1,27 @@
+<!-- This is ACME's generic look and feel, which is based on
+PNOME's look and feel. -->
+<html metal:define-macro="page"
+ metal:use-macro="pnome_macros_page">
+<head>
+<title metal:fill-slot="title">ACME Look and Feel</title>
+<metal:block>
+</metal:block>
+</head>
+<body>
+<div>
+ <div>
+ "The early bird gets the worm, but the second mouse gets the cheese."
+ </div>
+ <a href="#">Preferences...</a>
+</div>
+<div>
+ Content here
+</div>
+<div metal:fill-slot="page-footer">
+Copyright 2004 Acme Inc.
+<div metal:define-slot="disclaimer">
+Standard disclaimers apply.
+</div>
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/document_list.html b/src/zope/tal/tests/output/document_list.html
new file mode 100644
index 0000000..9e0ea10
--- /dev/null
+++ b/src/zope/tal/tests/output/document_list.html
@@ -0,0 +1,30 @@
+<!-- ACME's document_list uses the ACME look and feel -->
+<html>
+<head>
+<title>Acme Document List</title>
+<style type="text/css">
+ body { background-color: white; }
+</style>
+</head>
+<body>
+<div>
+ <div>
+ "The early bird gets the worm, but the second mouse gets the cheese."
+ </div>
+ <a href="#">Preferences...</a>
+</div>
+<div>
+<h1>Documents</h1>
+<ul>
+<li>Rocket Science for Dummies</li>
+<li>Birds for the Gourmet Chef</li>
+</ul>
+</div>
+<div>
+Copyright 2004 Acme Inc.
+<div>
+This document list is classified.
+</div>
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/document_list_source.html b/src/zope/tal/tests/output/document_list_source.html
new file mode 100644
index 0000000..69600e0
--- /dev/null
+++ b/src/zope/tal/tests/output/document_list_source.html
@@ -0,0 +1,30 @@
+<!-- ACME's document_list uses the ACME look and feel -->
+<html metal:use-macro="acme_macros_page">
+<head>
+<title metal:fill-slot="title">Acme Document List</title>
+<style metal:fill-slot="local-styles" type="text/css">
+ body { background-color: white; }
+</style>
+</head>
+<body>
+<div>
+ <div>
+ "The early bird gets the worm, but the second mouse gets the cheese."
+ </div>
+ <a href="#">Preferences...</a>
+</div>
+<div metal:fill-slot="content">
+<h1>Documents</h1>
+<ul>
+<li>Rocket Science for Dummies</li>
+<li>Birds for the Gourmet Chef</li>
+</ul>
+</div>
+<div>
+Copyright 2004 Acme Inc.
+<div metal:fill-slot="disclaimer">
+This document list is classified.
+</div>
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test01.html b/src/zope/tal/tests/output/test01.html
new file mode 100644
index 0000000..7064db0
--- /dev/null
+++ b/src/zope/tal/tests/output/test01.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+
+ <head>dadada</head>
+
+ <body>
+
+ <h1>This
+Is
+The
+Replaced
+Title</h1>
+
+ <!-- test entity references -->
+ &nbsp;&HarryPotter;
+
+ <!-- examples adapted from TemplateAttributeLanguageSyntax -->
+
+ <span>here/id</span>
+
+ <p>5</p>
+
+ <p>
+ honda
+ </p>
+ <p>
+ subaru
+ </p>
+ <p>
+ acura
+ </p>
+
+ <p xml:foo="bar">foo bar</p>
+
+ <!-- more examples -->
+
+ <ul>
+ <span>
+ <li>honda</li>
+ </span>
+ <span>
+ <li>subaru</li>
+ </span>
+ <span>
+ <li>acura</li>
+ </span>
+ </ul>
+
+ <!-- test attribute expansion -->
+
+ <a href="http://python.org">python</a>
+ <a href="http://python.org">python</a>
+
+ <!-- test insert/replace structure -->
+ <span></span>
+
+
+ <span />
+ <span />
+
+ &nbsp;<h3>Header Level 3</h3>
+ <span>&nbsp;<h3>Header Level 3</h3></span>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/output/test01.xml b/src/zope/tal/tests/output/test01.xml
new file mode 100644
index 0000000..91e9851
--- /dev/null
+++ b/src/zope/tal/tests/output/test01.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" ?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+
+ <head>dadada</head>
+
+ <body>
+
+ <h1>This Is The Replaced Title</h1>
+
+ <!-- test entity references -->
+ &nbsp;&HarryPotter;
+
+ <!-- examples adapted from TemplateAttributeLanguageSyntax -->
+
+ <span>here/id</span>
+
+ <p>5</p>
+
+ <p>
+ honda
+ </p>
+ <p>
+ subaru
+ </p>
+ <p>
+ acura
+ </p>
+
+ <p xml:foo="bar">foo bar</p>
+
+ <!-- more examples -->
+
+ <ul>
+ <span>
+ <li>honda</li>
+ </span>
+ <span>
+ <li>subaru</li>
+ </span>
+ <span>
+ <li>acura</li>
+ </span>
+ </ul>
+
+ <!-- test attribute expansion -->
+
+ <a href="http://python.org">python</a>
+ <a href="http://python.org">python</a>
+
+ <!-- test insert/replace structure -->
+ <span></span>
+
+
+ <span/>
+ <span/>
+
+ &nbsp;<h3>Header Level 3</h3>
+ <span>&nbsp;<h3>Header Level 3</h3></span>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/output/test02.html b/src/zope/tal/tests/output/test02.html
new file mode 100644
index 0000000..8d081fc
--- /dev/null
+++ b/src/zope/tal/tests/output/test02.html
@@ -0,0 +1,118 @@
+<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
+
+<foo:header xmlns:foo="whomping-willow" plain="guido" quote="&quot;" apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
+ <manifest>
+ <document>
+ <name>sample1</name>
+ <description>a simple invoice</description>
+ </document>
+ </manifest>
+</foo:header>
+
+<body>
+
+<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
+
+<invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
+ <header>
+ <invoicenumber>01786</invoicenumber>
+ <invoicedate>2000-03-17</invoicedate> <!-- March 17th, 2000 -->
+ <orderno>55377</orderno>
+ <orderdate>2000-03-15</orderdate> <!-- March 15th, 2000 -->
+ <customerpo>GJ03405</customerpo>
+ <shipmethod>DAVE 1</shipmethod>
+ <shipdate>2000-03-17</shipdate> <!-- March 17th, 2000 -->
+ <customerid>K5211(34)</customerid>
+ <salespersoncode>23</salespersoncode>
+ <taxid>23</taxid>
+ </header>
+ <invoiceto>
+ <name>SHIPWRIGHT RESTAURANTS LIMITED</name>
+ <addressline>125 NORTH SERVICE ROAD W</addressline>
+ <addressline>WESTLAKE ACCESS</addressline>
+ <city>NORTH BAY</city>
+ <postcode>L8B1O5</postcode>
+ <state>ONTARIO</state>
+ <country>CANADA</country>
+ </invoiceto>
+ <shipto>
+ <name />
+ <addressline>ATTN: PAULINE DEGRASSI</addressline>
+ <city />
+ <postcode />
+ <state />
+ <country />
+ </shipto>
+ <detaillines>
+ <detailline>
+ <quantityshipped>1</quantityshipped>
+ <unitofmeasure>CS</unitofmeasure>
+ <partnumber>DM 5309</partnumber>
+ <partdescription>#1013 12 OZ.MUNICH STEIN</partdescription>
+ <unitprice>37.72</unitprice>
+ <linetotal>37.72</linetotal>
+ </detailline>
+ <detailline>
+ <quantityshipped>6</quantityshipped>
+ <unitofmeasure>DZ</unitofmeasure>
+ <partnumber>ON 6420</partnumber>
+ <partdescription>PROVINCIAL DINNER FORK</partdescription>
+ <unitprice>17.98</unitprice>
+ <linetotal>107.88</linetotal>
+ </detailline>
+ <detailline>
+ <quantityshipped>72</quantityshipped>
+ <unitofmeasure>EA</unitofmeasure>
+ <partnumber>JR20643</partnumber>
+ <partdescription>PLASTIC HANDLED STEAK KNIFE</partdescription>
+ <unitprice>.81</unitprice>
+ <linetotal>58.32</linetotal>
+ </detailline>
+ <detailline>
+ <quantityshipped>6</quantityshipped>
+ <unitofmeasure>DZ</unitofmeasure>
+ <partnumber>ON 6410</partnumber>
+ <partdescription>PROVINCIAL TEASPOONS</partdescription>
+ <unitprice>12.16</unitprice>
+ <linetotal>72.96</linetotal>
+ </detailline>
+ <detailline>
+ <quantityshipped>0</quantityshipped>
+ <unitofmeasure>DZ</unitofmeasure>
+ <partnumber>ON 6411</partnumber>
+ <partdescription>PROVINCIAL RD BOWL SPOON</partdescription>
+ <quantitybackordered>6</quantitybackordered>
+ <unitprice>17.98</unitprice>
+ <linetotal>0.00</linetotal>
+ </detailline>
+ <detailline>
+ <quantityshipped>1</quantityshipped>
+ <unitofmeasure>EA</unitofmeasure>
+ <partnumber>DO 3218</partnumber>
+ <partdescription>34 OZ DUAL DIAL SCALE AM3218</partdescription>
+ <unitprice>70.00</unitprice>
+ <discountpercentage>5.0</discountpercentage>
+ <linetotal>66.50</linetotal>
+ </detailline>
+ <detailline>
+ <quantityshipped>1</quantityshipped>
+ <unitofmeasure>CS</unitofmeasure>
+ <partnumber>DM 195</partnumber>
+ <partdescription>20 OZ.BEER PUB GLASS</partdescription>
+ <unitprice>55.90</unitprice>
+ <linetotal>55.90</linetotal>
+ </detailline>
+ </detaillines>
+ <totals>
+ <subtotal>399.28</subtotal>
+ <discounttotal>3.50</discounttotal>
+ <freighttotal>23.75</freighttotal>
+ <gsttotal>29.61</gsttotal>
+ <provtaxtotal>33.84</provtaxtotal>
+ <othertotal>33.84</othertotal>
+ <invoicetotal>486.48</invoicetotal>
+ </totals>
+</invoice>
+
+</body>
+</biztalk_1>
diff --git a/src/zope/tal/tests/output/test02.xml b/src/zope/tal/tests/output/test02.xml
new file mode 100644
index 0000000..71ff075
--- /dev/null
+++ b/src/zope/tal/tests/output/test02.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" ?>
+<biztalk_1 xmlns="urn:schemas-biztalk-org:biztalk:biztalk_1">
+
+<foo:header xmlns:foo="whomping-willow" plain="guido" quote="&quot;" apostrophe="'" both="&quot;'" lt="&lt;" gt="&gt;" amp="&amp;" foo="">
+ <manifest>
+ <document>
+ <name>sample1</name>
+ <description>a simple invoice</description>
+ </document>
+ </manifest>
+</foo:header>
+
+<body>
+
+<!-- sample1.xml is an example of a simple invoice for a small restaurant supplies order -->
+
+<Invoice xmlns="urn:http://schemas.biztalk.org/united_rest_com/yw7sg15x.xml">
+ <Header>
+ <InvoiceNumber>01786</InvoiceNumber>
+ <InvoiceDate>2000-03-17</InvoiceDate> <!-- March 17th, 2000 -->
+ <OrderNo>55377</OrderNo>
+ <OrderDate>2000-03-15</OrderDate> <!-- March 15th, 2000 -->
+ <CustomerPO>GJ03405</CustomerPO>
+ <ShipMethod>DAVE 1</ShipMethod>
+ <ShipDate>2000-03-17</ShipDate> <!-- March 17th, 2000 -->
+ <CustomerID>K5211(34)</CustomerID>
+ <SalesPersonCode>23</SalesPersonCode>
+ <TaxID>23</TaxID>
+ </Header>
+ <InvoiceTo>
+ <Name>SHIPWRIGHT RESTAURANTS LIMITED</Name>
+ <AddressLine>125 NORTH SERVICE ROAD W</AddressLine>
+ <AddressLine>WESTLAKE ACCESS</AddressLine>
+ <City>NORTH BAY</City>
+ <PostCode>L8B1O5</PostCode>
+ <State>ONTARIO</State>
+ <Country>CANADA</Country>
+ </InvoiceTo>
+ <ShipTo>
+ <Name/>
+ <AddressLine>ATTN: PAULINE DEGRASSI</AddressLine>
+ <City/>
+ <PostCode/>
+ <State/>
+ <Country/>
+ </ShipTo>
+ <DetailLines>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>CS</UnitOfMeasure>
+ <PartNumber>DM 5309</PartNumber>
+ <PartDescription>#1013 12 OZ.MUNICH STEIN</PartDescription>
+ <UnitPrice>37.72</UnitPrice>
+ <LineTotal>37.72</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>6</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6420</PartNumber>
+ <PartDescription>PROVINCIAL DINNER FORK</PartDescription>
+ <UnitPrice>17.98</UnitPrice>
+ <LineTotal>107.88</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>72</QuantityShipped>
+ <UnitOfMeasure>EA</UnitOfMeasure>
+ <PartNumber>JR20643</PartNumber>
+ <PartDescription>PLASTIC HANDLED STEAK KNIFE</PartDescription>
+ <UnitPrice>.81</UnitPrice>
+ <LineTotal>58.32</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>6</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6410</PartNumber>
+ <PartDescription>PROVINCIAL TEASPOONS</PartDescription>
+ <UnitPrice>12.16</UnitPrice>
+ <LineTotal>72.96</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>0</QuantityShipped>
+ <UnitOfMeasure>DZ</UnitOfMeasure>
+ <PartNumber>ON 6411</PartNumber>
+ <PartDescription>PROVINCIAL RD BOWL SPOON</PartDescription>
+ <QuantityBackOrdered>6</QuantityBackOrdered>
+ <UnitPrice>17.98</UnitPrice>
+ <LineTotal>0.00</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>EA</UnitOfMeasure>
+ <PartNumber>DO 3218</PartNumber>
+ <PartDescription>34 OZ DUAL DIAL SCALE AM3218</PartDescription>
+ <UnitPrice>70.00</UnitPrice>
+ <DiscountPercentage>5.0</DiscountPercentage>
+ <LineTotal>66.50</LineTotal>
+ </DetailLine>
+ <DetailLine>
+ <QuantityShipped>1</QuantityShipped>
+ <UnitOfMeasure>CS</UnitOfMeasure>
+ <PartNumber>DM 195</PartNumber>
+ <PartDescription>20 OZ.BEER PUB GLASS</PartDescription>
+ <UnitPrice>55.90</UnitPrice>
+ <LineTotal>55.90</LineTotal>
+ </DetailLine>
+ </DetailLines>
+ <Totals>
+ <SubTotal>399.28</SubTotal>
+ <DiscountTotal>3.50</DiscountTotal>
+ <FreightTotal>23.75</FreightTotal>
+ <GSTTotal>29.61</GSTTotal>
+ <ProvTaxTotal>33.84</ProvTaxTotal>
+ <OtherTotal>33.84</OtherTotal>
+ <InvoiceTotal>486.48</InvoiceTotal>
+ </Totals>
+</Invoice>
+
+</body>
+</biztalk_1>
diff --git a/src/zope/tal/tests/output/test03.html b/src/zope/tal/tests/output/test03.html
new file mode 100644
index 0000000..7fb5156
--- /dev/null
+++ b/src/zope/tal/tests/output/test03.html
@@ -0,0 +1,9 @@
+<p>
+ <span>
+ <span>hello brave new world</span>
+ <span>
+ <span>goodbye cruel world</span>
+ </span>
+ <span>hello brave new world</span>
+ </span>
+</p>
diff --git a/src/zope/tal/tests/output/test03.xml b/src/zope/tal/tests/output/test03.xml
new file mode 100644
index 0000000..24be638
--- /dev/null
+++ b/src/zope/tal/tests/output/test03.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<p>
+ <span>
+ <span>hello brave new world</span>
+ <span>
+ <span>goodbye cruel world</span>
+ </span>
+ <span>hello brave new world</span>
+ </span>
+</p>
diff --git a/src/zope/tal/tests/output/test04.html b/src/zope/tal/tests/output/test04.html
new file mode 100644
index 0000000..f0666da
--- /dev/null
+++ b/src/zope/tal/tests/output/test04.html
@@ -0,0 +1,38 @@
+<html>
+
+ <body>
+
+ <ul>
+ </ul>
+
+ <span />
+
+ <ul>
+ <li>
+ 0
+ hello world
+ </li>
+ <li>
+ 1
+ hello world
+ </li>
+ </ul>
+
+ <span />
+
+ <ul>
+ <li>
+ 0
+ goodbye cruel world
+ </li>
+ <li>
+ 1
+ goodbye cruel world
+ </li>
+ </ul>
+
+ <p>define-slot</p>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/output/test04.xml b/src/zope/tal/tests/output/test04.xml
new file mode 100644
index 0000000..8b73d02
--- /dev/null
+++ b/src/zope/tal/tests/output/test04.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" ?>
+<html>
+
+ <body>
+
+ <ul>
+ </ul>
+
+ <span/>
+
+ <ul>
+ <li>
+ 0
+ hello world
+ </li>
+ <li>
+ 1
+ hello world
+ </li>
+ </ul>
+
+ <span/>
+
+ <ul>
+ <li>
+ 0
+ goodbye cruel world
+ </li>
+ <li>
+ 1
+ goodbye cruel world
+ </li>
+ </ul>
+
+ <p>define-slot</p>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/output/test05.html b/src/zope/tal/tests/output/test05.html
new file mode 100644
index 0000000..006851a
--- /dev/null
+++ b/src/zope/tal/tests/output/test05.html
@@ -0,0 +1,9 @@
+<html>
+
+ <body>
+
+ <h1>This is the body of test5</h1>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/output/test05.xml b/src/zope/tal/tests/output/test05.xml
new file mode 100644
index 0000000..0bc2691
--- /dev/null
+++ b/src/zope/tal/tests/output/test05.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" ?>
+<html>
+
+ <body>
+
+ <h1>This is the body of test5</h1>
+
+ </body>
+
+</html>
diff --git a/src/zope/tal/tests/output/test06.html b/src/zope/tal/tests/output/test06.html
new file mode 100644
index 0000000..d3f58d9
--- /dev/null
+++ b/src/zope/tal/tests/output/test06.html
@@ -0,0 +1,7 @@
+<html>
+ <body>
+
+ <h1>This is the body of test5</h1>
+
+ </body>
+</html>
diff --git a/src/zope/tal/tests/output/test06.xml b/src/zope/tal/tests/output/test06.xml
new file mode 100644
index 0000000..b9ad4ac
--- /dev/null
+++ b/src/zope/tal/tests/output/test06.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" ?>
+<html>
+ <body>
+
+ <h1>This is the body of test5</h1>
+
+ </body>
+</html>
diff --git a/src/zope/tal/tests/output/test07.html b/src/zope/tal/tests/output/test07.html
new file mode 100644
index 0000000..e0b3d88
--- /dev/null
+++ b/src/zope/tal/tests/output/test07.html
@@ -0,0 +1,11 @@
+<table>
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span>Bottom Right</span></td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/output/test07.xml b/src/zope/tal/tests/output/test07.xml
new file mode 100644
index 0000000..8884d97
--- /dev/null
+++ b/src/zope/tal/tests/output/test07.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" ?>
+<table>
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span>Bottom Right</span></td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/output/test08.html b/src/zope/tal/tests/output/test08.html
new file mode 100644
index 0000000..06e01b2
--- /dev/null
+++ b/src/zope/tal/tests/output/test08.html
@@ -0,0 +1,47 @@
+<table>
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span>
+ <h1>Some headline</h1>
+ <p>This is the real contents of the bottom right slot.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ </span></td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/output/test08.xml b/src/zope/tal/tests/output/test08.xml
new file mode 100644
index 0000000..51a969c
--- /dev/null
+++ b/src/zope/tal/tests/output/test08.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" ?>
+<table>
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span>
+ <h1>Some headline</h1>
+ <p>This is the real contents of the bottom right slot.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ </span></td>
+ </tr>
+</table>
diff --git a/src/zope/tal/tests/output/test09.html b/src/zope/tal/tests/output/test09.html
new file mode 100644
index 0000000..844c1a9
--- /dev/null
+++ b/src/zope/tal/tests/output/test09.html
@@ -0,0 +1,30 @@
+<html>
+<body>
+<p>
+ Just a bunch of text.</p>
+<p>more text...</p>
+<ul>
+ <li>first item</li>
+ <li>second item
+
+ <ol>
+ <li>second list, first item</li>
+ <li>second list, second item
+ <dl compact>
+ <dt>term 1</dt>
+ <dt>term 2</dt>
+ <dd>definition</dd>
+ </dl></li>
+ </ol></li>
+
+ <li>Now let's have a paragraph...
+ <p>My Paragraph</p>
+ </li>
+
+ <li>And a table in a list item:
+ <table>
+ </table></li>
+</ul>
+
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test09.xml b/src/zope/tal/tests/output/test09.xml
new file mode 100644
index 0000000..c3d10d7
--- /dev/null
+++ b/src/zope/tal/tests/output/test09.xml
@@ -0,0 +1,30 @@
+<html>
+<body>
+<p>
+ Just a bunch of text.</p>
+<p>more text...</p>
+<ul>
+ <li>first item</li>
+ <li>second item
+
+ <ol>
+ <li>second list, first item</li>
+ <li>second list, second item
+ <dl compact="">
+ <dt>term 1</dt>
+ <dt>term 2</dt>
+ <dd>definition</dd>
+ </dl></li>
+ </ol></li>
+
+ <li>Now let's have a paragraph...
+ <p>My Paragraph</p>
+ </li>
+
+ <li>And a table in a list item:
+ <table>
+ </table></li>
+</ul>
+
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test10.html b/src/zope/tal/tests/output/test10.html
new file mode 100644
index 0000000..d9cc7ed
--- /dev/null
+++ b/src/zope/tal/tests/output/test10.html
@@ -0,0 +1,51 @@
+<html><body>
+<table>
+<!-- macro definition with slots -->
+ <tr>
+ <td>Top Left</td>
+ <td>Top Right</td>
+ </tr>
+ <tr>
+ <td>Bottom left</td>
+ <td><span>
+ <h1>Some headline</h1>
+ <p>This is the real contents of the bottom right slot.</p>
+ <hr>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <p>It is supposed to contain a lot of text. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb. Blah, blah, blab.
+ Blabber, blabber, blah. Baah, baah, barb.</p>
+ <br><br>
+ </span></td>
+ </tr>
+</table>
+</body></html>
diff --git a/src/zope/tal/tests/output/test11.html b/src/zope/tal/tests/output/test11.html
new file mode 100644
index 0000000..9e2223c
--- /dev/null
+++ b/src/zope/tal/tests/output/test11.html
@@ -0,0 +1,8 @@
+<html>
+ <a href="http://www.python.org">bar</a>
+ <p>bad boy!</p>
+ <p>x undefined</p>
+ x undefined
+ x undefined
+ <hr />
+</html>
diff --git a/src/zope/tal/tests/output/test11.xml b/src/zope/tal/tests/output/test11.xml
new file mode 100644
index 0000000..caba039
--- /dev/null
+++ b/src/zope/tal/tests/output/test11.xml
@@ -0,0 +1,5 @@
+<html>
+ <a href="http://www.python.org">bar</a>
+ <p>bad boy!</p>
+ <p>x undefined</p>
+</html>
diff --git a/src/zope/tal/tests/output/test12.html b/src/zope/tal/tests/output/test12.html
new file mode 100644
index 0000000..9533b42
--- /dev/null
+++ b/src/zope/tal/tests/output/test12.html
@@ -0,0 +1,24 @@
+<span />
+
+<img ismap>
+<img ismap="ismap">
+<img ismap="ismap">
+<img ismap="foo">
+
+<img ismap="ismap">
+<img>
+<img>
+
+<img ismap="ismap">
+<img>
+<img>
+
+<img ismap="ismap">
+<img>
+<img>
+
+<span />
+
+<img src="foo">
+<img src="x.gif">
+<img>
diff --git a/src/zope/tal/tests/output/test13.html b/src/zope/tal/tests/output/test13.html
new file mode 100644
index 0000000..d68e0ce
--- /dev/null
+++ b/src/zope/tal/tests/output/test13.html
@@ -0,0 +1,7 @@
+Here's a stray greater than: >
+
+<script>
+ <!-- no comment -->
+ <notag>
+ &noentity;
+</script>
diff --git a/src/zope/tal/tests/output/test14.html b/src/zope/tal/tests/output/test14.html
new file mode 100644
index 0000000..b9bf468
--- /dev/null
+++ b/src/zope/tal/tests/output/test14.html
@@ -0,0 +1,13 @@
+<table>
+ <tr>
+ <td>car</td>
+ <td>bike</td>
+ <td>broomstick</td>
+ </tr>
+</table>
+
+<p>
+ Harry
+ Ron
+ Hermione
+</p>
diff --git a/src/zope/tal/tests/output/test14.xml b/src/zope/tal/tests/output/test14.xml
new file mode 100644
index 0000000..67c0c37
--- /dev/null
+++ b/src/zope/tal/tests/output/test14.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" ?>
+<html>
+
+<table>
+ <tr>
+ <td>car</td>
+ <td>bike</td>
+ <td>broomstick</td>
+ </tr>
+</table>
+
+<p>
+ Harry
+ Ron
+ Hermione
+</p>
+
+</html>
diff --git a/src/zope/tal/tests/output/test15.html b/src/zope/tal/tests/output/test15.html
new file mode 100644
index 0000000..314fd43
--- /dev/null
+++ b/src/zope/tal/tests/output/test15.html
@@ -0,0 +1,29 @@
+<span>
+ <span>INNERSLOT</span>
+</span>
+
+<span>
+ <xxx>inner-argument</xxx>
+</span>
+
+<div>
+<span>
+ <xxx>
+ OUTERSLOT
+ </xxx>
+</span>
+</div>
+
+<div>
+<span>
+ <div>outer-argument</div>
+</span>
+</div>
+
+<div>
+<span>
+ <xxx>
+ OUTERSLOT
+ </xxx>
+</span>
+</div>
diff --git a/src/zope/tal/tests/output/test16.html b/src/zope/tal/tests/output/test16.html
new file mode 100644
index 0000000..d3ea228
--- /dev/null
+++ b/src/zope/tal/tests/output/test16.html
@@ -0,0 +1 @@
+<a href="/base/valid/link.html">blah, blah</a>
diff --git a/src/zope/tal/tests/output/test16.xml b/src/zope/tal/tests/output/test16.xml
new file mode 100644
index 0000000..77e9069
--- /dev/null
+++ b/src/zope/tal/tests/output/test16.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<body>
+
+<ImG href="foo" Alt="bar" alT="baz" Href="about:foo"/>
+
+</body>
diff --git a/src/zope/tal/tests/output/test17.html b/src/zope/tal/tests/output/test17.html
new file mode 100644
index 0000000..e50997d
--- /dev/null
+++ b/src/zope/tal/tests/output/test17.html
@@ -0,0 +1,6 @@
+Yes
+Yes
+Yes
+
+Yes
+Yes
diff --git a/src/zope/tal/tests/output/test17.xml b/src/zope/tal/tests/output/test17.xml
new file mode 100644
index 0000000..7a54cdb
--- /dev/null
+++ b/src/zope/tal/tests/output/test17.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+<body>
+Yes
+Yes
+Yes
+
+Yes
+Yes
+</body>
diff --git a/src/zope/tal/tests/output/test18.html b/src/zope/tal/tests/output/test18.html
new file mode 100644
index 0000000..f49e29e
--- /dev/null
+++ b/src/zope/tal/tests/output/test18.html
@@ -0,0 +1,16 @@
+Content
+
+
+
+Content
+
+
+
+<p>Content</p>
+<p></p>
+<img>
+
+Yes
+Yes
+Yes
+Yes
diff --git a/src/zope/tal/tests/output/test18.xml b/src/zope/tal/tests/output/test18.xml
new file mode 100644
index 0000000..77eba02
--- /dev/null
+++ b/src/zope/tal/tests/output/test18.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<body>
+Content
+
+
+
+Content
+
+
+
+<p>Content</p>
+<p/>
+<img/>
+
+Yes
+Yes
+Yes
+Yes
+</body>
diff --git a/src/zope/tal/tests/output/test19.html b/src/zope/tal/tests/output/test19.html
new file mode 100644
index 0000000..2341a4a
--- /dev/null
+++ b/src/zope/tal/tests/output/test19.html
@@ -0,0 +1,3 @@
+<span>REPLACE THIS</span>
+<span>MSGID</span>
+<span>AND ANOTHER TRANSLATED STRING</span>
diff --git a/src/zope/tal/tests/output/test19.xml b/src/zope/tal/tests/output/test19.xml
new file mode 100644
index 0000000..4460acd
--- /dev/null
+++ b/src/zope/tal/tests/output/test19.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<body>
+<span>REPLACE THIS</span>
+<span>MSGID</span>
+<span>AND ANOTHER TRANSLATED STRING</span>
+</body>
diff --git a/src/zope/tal/tests/output/test20.html b/src/zope/tal/tests/output/test20.html
new file mode 100644
index 0000000..606b989
--- /dev/null
+++ b/src/zope/tal/tests/output/test20.html
@@ -0,0 +1 @@
+<span>REPLACEABLE HERE</span>
diff --git a/src/zope/tal/tests/output/test20.xml b/src/zope/tal/tests/output/test20.xml
new file mode 100644
index 0000000..ed1f9fe
--- /dev/null
+++ b/src/zope/tal/tests/output/test20.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<body>
+<span>REPLACEABLE HERE</span>
+</body>
diff --git a/src/zope/tal/tests/output/test21.html b/src/zope/tal/tests/output/test21.html
new file mode 100644
index 0000000..95b3b08
--- /dev/null
+++ b/src/zope/tal/tests/output/test21.html
@@ -0,0 +1 @@
+<span>Lomax WAS BORN IN Antarctica.</span>
diff --git a/src/zope/tal/tests/output/test21.xml b/src/zope/tal/tests/output/test21.xml
new file mode 100644
index 0000000..c373d52
--- /dev/null
+++ b/src/zope/tal/tests/output/test21.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<body>
+<span>Lomax WAS BORN IN Antarctica.</span>
+</body>
diff --git a/src/zope/tal/tests/output/test22.html b/src/zope/tal/tests/output/test22.html
new file mode 100644
index 0000000..6c1b6de
--- /dev/null
+++ b/src/zope/tal/tests/output/test22.html
@@ -0,0 +1 @@
+<span><b>Jim</b> WAS BORN IN the USA.</span>
diff --git a/src/zope/tal/tests/output/test22.xml b/src/zope/tal/tests/output/test22.xml
new file mode 100644
index 0000000..c2e79c5
--- /dev/null
+++ b/src/zope/tal/tests/output/test22.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<body>
+ <span>content</span>
+ omit
+ replace
+</body>
diff --git a/src/zope/tal/tests/output/test23.html b/src/zope/tal/tests/output/test23.html
new file mode 100644
index 0000000..0ea1654
--- /dev/null
+++ b/src/zope/tal/tests/output/test23.html
@@ -0,0 +1 @@
+<span>59 minutes after 6 PM</span>
diff --git a/src/zope/tal/tests/output/test24.html b/src/zope/tal/tests/output/test24.html
new file mode 100644
index 0000000..8dbfba3
--- /dev/null
+++ b/src/zope/tal/tests/output/test24.html
@@ -0,0 +1,7 @@
+<input name="DELETE_BUTTON">
+
+<input name="MESSAGE-ID">
+
+<input name="MESSAGE-ID" attr="INPUT-ATTR">
+
+<input name="MESSAGE-ID" attr="INPUT-ATTR">
diff --git a/src/zope/tal/tests/output/test25.html b/src/zope/tal/tests/output/test25.html
new file mode 100644
index 0000000..6b80bd3
--- /dev/null
+++ b/src/zope/tal/tests/output/test25.html
@@ -0,0 +1 @@
+<input name="DELETE">
diff --git a/src/zope/tal/tests/output/test26.html b/src/zope/tal/tests/output/test26.html
new file mode 100644
index 0000000..9d179a6
--- /dev/null
+++ b/src/zope/tal/tests/output/test26.html
@@ -0,0 +1 @@
+<span>7 is the JOB NUMBER</span>
diff --git a/src/zope/tal/tests/output/test27.html b/src/zope/tal/tests/output/test27.html
new file mode 100644
index 0000000..96229e4
--- /dev/null
+++ b/src/zope/tal/tests/output/test27.html
@@ -0,0 +1 @@
+<p>Your contact email address is recorded as <a href="mailto:user@example.com">aperson@dom.ain</a></p>
diff --git a/src/zope/tal/tests/output/test28.html b/src/zope/tal/tests/output/test28.html
new file mode 100644
index 0000000..96229e4
--- /dev/null
+++ b/src/zope/tal/tests/output/test28.html
@@ -0,0 +1 @@
+<p>Your contact email address is recorded as <a href="mailto:user@example.com">aperson@dom.ain</a></p>
diff --git a/src/zope/tal/tests/output/test29.html b/src/zope/tal/tests/output/test29.html
new file mode 100644
index 0000000..886137e
--- /dev/null
+++ b/src/zope/tal/tests/output/test29.html
@@ -0,0 +1 @@
+<div>AT THE TONE THE TIME WILL BE <span>59 minutes after 6 PM</span>... BEEP!</div>
diff --git a/src/zope/tal/tests/output/test30.html b/src/zope/tal/tests/output/test30.html
new file mode 100644
index 0000000..964b772
--- /dev/null
+++ b/src/zope/tal/tests/output/test30.html
@@ -0,0 +1 @@
+<p>Your contact email address is recorded as <a href="mailto:${request/submitter}">aperson@dom.ain</a></p>
diff --git a/src/zope/tal/tests/output/test31.html b/src/zope/tal/tests/output/test31.html
new file mode 100644
index 0000000..964b772
--- /dev/null
+++ b/src/zope/tal/tests/output/test31.html
@@ -0,0 +1 @@
+<p>Your contact email address is recorded as <a href="mailto:${request/submitter}">aperson@dom.ain</a></p>
diff --git a/src/zope/tal/tests/output/test32.html b/src/zope/tal/tests/output/test32.html
new file mode 100644
index 0000000..f39bd97
--- /dev/null
+++ b/src/zope/tal/tests/output/test32.html
@@ -0,0 +1 @@
+<span><span>Lomax</span> was born in <span>Antarctica</span></span>
diff --git a/src/zope/tal/tests/output/test33.html b/src/zope/tal/tests/output/test33.html
new file mode 100644
index 0000000..4472f21
--- /dev/null
+++ b/src/zope/tal/tests/output/test33.html
@@ -0,0 +1 @@
+<span>don't translate me</span>
diff --git a/src/zope/tal/tests/output/test34.html b/src/zope/tal/tests/output/test34.html
new file mode 100644
index 0000000..1d7b5f2
--- /dev/null
+++ b/src/zope/tal/tests/output/test34.html
@@ -0,0 +1,7 @@
+<span>
+ stuff
+ foobar
+ more stuff
+</span>
+
+<span>STUFF foobar MORE STUFF</span>
diff --git a/src/zope/tal/tests/output/test35.html b/src/zope/tal/tests/output/test35.html
new file mode 100644
index 0000000..b1a9d2e
--- /dev/null
+++ b/src/zope/tal/tests/output/test35.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+ <h1>page</h1>
diff --git a/src/zope/tal/tests/output/test36.html b/src/zope/tal/tests/output/test36.html
new file mode 100644
index 0000000..2a563c1
--- /dev/null
+++ b/src/zope/tal/tests/output/test36.html
@@ -0,0 +1,2 @@
+&lt;foo&gt;
+<span>&lt;foo&gt; <bar /> <b>some</b> <i>text</i></span>
diff --git a/src/zope/tal/tests/output/test_domain.html b/src/zope/tal/tests/output/test_domain.html
new file mode 100644
index 0000000..6a282ac
--- /dev/null
+++ b/src/zope/tal/tests/output/test_domain.html
@@ -0,0 +1,5 @@
+<div>
+<span>replace this</span>
+<span>msgid</span>
+<span>and another translated string</span>
+</div>
diff --git a/src/zope/tal/tests/output/test_failed_attr_translation.html b/src/zope/tal/tests/output/test_failed_attr_translation.html
new file mode 100644
index 0000000..cd34b1f
--- /dev/null
+++ b/src/zope/tal/tests/output/test_failed_attr_translation.html
@@ -0,0 +1 @@
+<input value="don't translate me">
diff --git a/src/zope/tal/tests/output/test_metal1.html b/src/zope/tal/tests/output/test_metal1.html
new file mode 100644
index 0000000..c8cc346
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal1.html
@@ -0,0 +1,79 @@
+<span metal:define-macro="OUTER">
+ AAA
+ <span metal:define-macro="INNER">INNER</span>
+ BBB
+</span>
+
+<span metal:use-macro="OUTER">
+ AAA
+ <span>INNER</span>
+ BBB
+</span>
+
+<span metal:use-macro="INNER">INNER</span>
+
+<span metal:define-macro="OUTER2">
+ AAA
+ <xxx metal:define-slot="OUTERSLOT">
+ <span metal:define-macro="INNER2">INNER</span>
+ </xxx>
+ BBB
+</span>
+
+<span metal:use-macro="OUTER2">
+ AAA
+ <xxx>
+ <span>INNER</span>
+ </xxx>
+ BBB
+</span>
+
+<span metal:use-macro="INNER2">INNER</span>
+
+<span metal:use-macro="OUTER2">
+ AAA
+ <yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
+ BBB
+</span>
+
+<span metal:define-macro="OUTER3">
+ AAA
+ <xxx metal:define-slot="OUTERSLOT">
+ <span metal:define-macro="INNER3">INNER
+ <xxx metal:define-slot="INNERSLOT">INNERSLOT</xxx>
+ </span>
+ </xxx>
+ BBB
+</span>
+
+<span metal:use-macro="OUTER3">
+ AAA
+ <xxx>
+ <span>INNER
+ <xxx>INNERSLOT</xxx>
+ </span>
+ </xxx>
+ BBB
+</span>
+
+<span metal:use-macro="OUTER3">
+ AAA
+ <yyy metal:fill-slot="OUTERSLOT">OUTERSLOT</yyy>
+ BBB
+</span>
+
+<span metal:use-macro="INNER3">INNER
+ <xxx>INNERSLOT</xxx>
+ </span>
+
+<span metal:use-macro="INNER3">INNER
+ <yyy metal:fill-slot="INNERSLOT">INNERSLOT</yyy>
+ </span>
+
+<span metal:use-macro="INNER3">INNER
+ <yyy metal:fill-slot="INNERSLOT">
+ <zzz metal:define-macro="INSLOT">INSLOT</zzz>
+ </yyy>
+ </span>
+
+<zzz metal:use-macro="INSLOT">INSLOT</zzz>
diff --git a/src/zope/tal/tests/output/test_metal2.html b/src/zope/tal/tests/output/test_metal2.html
new file mode 100644
index 0000000..7e56c0c
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal2.html
@@ -0,0 +1,11 @@
+<div metal:define-macro="OUTER">
+ OUTER
+ <span metal:define-macro="INNER">INNER</span>
+ OUTER
+</div>
+
+<div metal:use-macro="OUTER">
+ OUTER
+ <span>INNER</span>
+ OUTER
+</div>
diff --git a/src/zope/tal/tests/output/test_metal3.html b/src/zope/tal/tests/output/test_metal3.html
new file mode 100644
index 0000000..b0af907
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal3.html
@@ -0,0 +1 @@
+<span tal:attributes="class string:foo">Should not get attr in metal</span>
diff --git a/src/zope/tal/tests/output/test_metal4.html b/src/zope/tal/tests/output/test_metal4.html
new file mode 100644
index 0000000..dc774d3
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal4.html
@@ -0,0 +1,4 @@
+<!-- the outer element *must* be tal:something or metal:something -->
+<metal:block define-macro="page" i18n:domain="zope">
+ <title metal:define-slot="title">Z3 UI</title>
+</metal:block>
diff --git a/src/zope/tal/tests/output/test_metal5.html b/src/zope/tal/tests/output/test_metal5.html
new file mode 100644
index 0000000..8bae3d8
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal5.html
@@ -0,0 +1,4 @@
+<!-- the outer element *must* include tal:omit-tag='' -->
+<x tal:omit-tag="" metal:define-macro="page" i18n:domain="zope">
+ <title metal:define-slot="title">Z3 UI</title>
+</x>
diff --git a/src/zope/tal/tests/output/test_metal6.html b/src/zope/tal/tests/output/test_metal6.html
new file mode 100644
index 0000000..ce243f2
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal6.html
@@ -0,0 +1,5 @@
+<metal:block define-macro="page">
+ <html i18:domain="zope">
+ <metal:block define-slot="title">Z3 UI</metal:block>
+ </html>
+</metal:block>
diff --git a/src/zope/tal/tests/output/test_metal7.html b/src/zope/tal/tests/output/test_metal7.html
new file mode 100644
index 0000000..cc449ed
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal7.html
@@ -0,0 +1,6 @@
+<html metal:define-macro="page" i18n:domain="zope">
+ <x metal:define-slot="title" />
+</html>
+<html metal:use-macro="page" i18n:domain="zope">
+ <x metal:fill-slot="title" />
+</html>
diff --git a/src/zope/tal/tests/output/test_metal8.html b/src/zope/tal/tests/output/test_metal8.html
new file mode 100644
index 0000000..d56adab
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal8.html
@@ -0,0 +1,19 @@
+<html metal:define-macro="page" i18n:domain="zope">
+<body>
+<div metal:define-macro="workspace">
+<div metal:define-slot="body">
+Default body
+</div>
+</div>
+</body>
+</html>
+
+<html metal:use-macro="page" i18n:domain="zope">
+<body>
+<div>
+<div metal:fill-slot="body">
+Filled-in body
+</div>
+</div>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_metal9.html b/src/zope/tal/tests/output/test_metal9.html
new file mode 100644
index 0000000..4cbc637
--- /dev/null
+++ b/src/zope/tal/tests/output/test_metal9.html
@@ -0,0 +1,32 @@
+<div metal:define-macro="macro1" i18n:domain="zope">
+<span metal:define-slot="slot1">
+Default for macro1
+</span>
+</div>
+
+<div metal:define-macro="macro2" metal:use-macro="macro1" i18n:domain="zope">
+<span metal:fill-slot="slot1">
+Macro 2's slot 1 decoration
+<span metal:define-slot="slot1">
+Default for macro2
+</span>
+</span>
+</div>
+
+<div metal:use-macro="macro2" i18n:domain="zope">
+<span metal:fill-slot="slot1">
+Macro 2's slot 1 decoration
+<span>
+Default for macro2
+</span>
+</span>
+</div>
+
+<div metal:use-macro="macro2" i18n:domain="zope">
+<span metal:fill-slot="slot1">
+Macro 2's slot 1 decoration
+<span metal:fill-slot="slot1">
+Custom slot1
+</span>
+</span>
+</div>
diff --git a/src/zope/tal/tests/output/test_sa1.html b/src/zope/tal/tests/output/test_sa1.html
new file mode 100644
index 0000000..a37b9e9
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa1.html
@@ -0,0 +1,10 @@
+<!--
+==============================================================================
+tests/input/test_sa1.html
+==============================================================================
+--><html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_sa1.xml b/src/zope/tal/tests/output/test_sa1.xml
new file mode 100644
index 0000000..8e1f4cc
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa1.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" ?><!--
+==============================================================================
+tests/input/test_sa1.xml
+==============================================================================
+-->
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_sa2.html b/src/zope/tal/tests/output/test_sa2.html
new file mode 100644
index 0000000..4709b49
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa2.html
@@ -0,0 +1,13 @@
+<!--
+==============================================================================
+tests/input/test_sa2.html
+==============================================================================
+--><!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_sa2.xml b/src/zope/tal/tests/output/test_sa2.xml
new file mode 100644
index 0000000..30b5699
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa2.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" ?><!--
+==============================================================================
+tests/input/test_sa2.xml
+==============================================================================
+-->
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "DTD/xhtml1-transitional.dtd">
+<html>
+<title>Simple test of source annotations</title>
+<body>
+<p>Foo!</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_sa3.html b/src/zope/tal/tests/output/test_sa3.html
new file mode 100644
index 0000000..8431438
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa3.html
@@ -0,0 +1,42 @@
+<!--
+==============================================================================
+tests/input/test_sa3.html
+==============================================================================
+--><html>
+<body>
+ <!--
+==============================================================================
+tests/input/test_sa3.html (line 3)
+==============================================================================
+--><div>This is macro1 on sa3 line 3.
+ <span>This is slot1 on sa3 line 4.</span><!--
+==============================================================================
+tests/input/test_sa3.html (line 4)
+==============================================================================
+-->
+ This is the end of macro1 on sa3 line 5.
+ </div>
+ <p>Some text on sa3 line 7.</p>
+ <!--
+==============================================================================
+tests/input/test_sa3.html (line 3)
+==============================================================================
+--><div>This is macro1 on sa3 line 3.
+ <!--
+==============================================================================
+tests/input/test_sa3.html (line 10)
+==============================================================================
+--><b>Text from sa3 line 10 is filled into slot1.</b><!--
+==============================================================================
+tests/input/test_sa3.html (line 4)
+==============================================================================
+-->
+ This is the end of macro1 on sa3 line 5.
+ </div><!--
+==============================================================================
+tests/input/test_sa3.html (line 12)
+==============================================================================
+-->
+ <p>This is some text on sa3 line 13.</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_sa3.xml b/src/zope/tal/tests/output/test_sa3.xml
new file mode 100644
index 0000000..bd20f83
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa3.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" ?><!--
+==============================================================================
+tests/input/test_sa3.xml
+==============================================================================
+-->
+<html>
+<body>
+ <!--
+==============================================================================
+tests/input/test_sa3.xml (line 4)
+==============================================================================
+--><div>This is macro1 on sa3 line 4.
+ <span>This is slot1 on sa3 line 5.</span><!--
+==============================================================================
+tests/input/test_sa3.xml (line 5)
+==============================================================================
+-->
+ This is the end of macro1 on sa3 line 6.
+ </div>
+ <p>Some text on sa3 line 8.</p>
+ <!--
+==============================================================================
+tests/input/test_sa3.xml (line 4)
+==============================================================================
+--><div>This is macro1 on sa3 line 4.
+ <!--
+==============================================================================
+tests/input/test_sa3.xml (line 11)
+==============================================================================
+--><b>Text from sa3 line 11 is filled into slot1.</b><!--
+==============================================================================
+tests/input/test_sa3.xml (line 5)
+==============================================================================
+-->
+ This is the end of macro1 on sa3 line 6.
+ </div><!--
+==============================================================================
+tests/input/test_sa3.xml (line 13)
+==============================================================================
+-->
+ <p>This is some text on sa3 line 14.</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/output/test_sa4.html b/src/zope/tal/tests/output/test_sa4.html
new file mode 100644
index 0000000..4aca908
--- /dev/null
+++ b/src/zope/tal/tests/output/test_sa4.html
@@ -0,0 +1,30 @@
+<!--
+==============================================================================
+tests/input/test_sa4.html
+==============================================================================
+--><html>
+<body>
+ <p>Some text on sa4 line 3.</p>
+ <!--
+==============================================================================
+tests/input/test_sa3.html (line 3)
+==============================================================================
+--><div>This is macro1 on sa3 line 3.
+ <!--
+==============================================================================
+tests/input/test_sa4.html (line 6)
+==============================================================================
+--><b>Text from sa4 line 6 is filled into slot1.</b><!--
+==============================================================================
+tests/input/test_sa3.html (line 4)
+==============================================================================
+-->
+ This is the end of macro1 on sa3 line 5.
+ </div><!--
+==============================================================================
+tests/input/test_sa4.html (line 8)
+==============================================================================
+-->
+ <p>This is some text on sa4 line 9.</p>
+</body>
+</html>
diff --git a/src/zope/tal/tests/run.py b/src/zope/tal/tests/run.py
new file mode 100644
index 0000000..b4dab8c
--- /dev/null
+++ b/src/zope/tal/tests/run.py
@@ -0,0 +1,45 @@
+#! /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.1 (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.
+
+$Id$
+"""
+import sys
+import unittest
+
+from zope.tal.tests import utils
+from zope.tal.tests import test_htmltalparser
+from zope.tal.tests import test_talinterpreter
+from zope.tal.tests import test_files
+from zope.tal.tests import test_sourcepos
+
+# TODO this code isn't picked up by the Zope 3 test framework..
+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)
diff --git a/src/zope/tal/tests/test_files.py b/src/zope/tal/tests/test_files.py
new file mode 100644
index 0000000..e26f00a
--- /dev/null
+++ b/src/zope/tal/tests/test_files.py
@@ -0,0 +1,90 @@
+##############################################################################
+#
+# 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.1 (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.
+
+$Id$
+"""
+
+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_sa'):
+ sys.argv = ["", "-Q", "-a", self.__file]
+ elif 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)
diff --git a/src/zope/tal/tests/test_htmltalparser.py b/src/zope/tal/tests/test_htmltalparser.py
new file mode 100644
index 0000000..eb53f51
--- /dev/null
+++ b/src/zope/tal/tests/test_htmltalparser.py
@@ -0,0 +1,1021 @@
+##############################################################################
+#
+# 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.1 (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.
+
+$Id$
+"""
+import pprint
+import sys
+import unittest
+
+from zope.tal import htmltalparser, 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)
+ program = self._merge(program, self.final_program)
+ self.assert_(got_program == program,
+ "Program:\n" + pprint.pformat(got_program)
+ + "\nExpected:\n" + pprint.pformat(program))
+ self.assert_(got_macros == macros,
+ "Macros:\n" + pprint.pformat(got_macros)
+ + "\nExpected:\n" + pprint.pformat(macros))
+
+ def _should_error(self, source, exc=taldefs.TALError):
+ def parse(self=self, source=source):
+ parser = htmltalparser.HTMLTALParser()
+ parser.parseString(self.prologue + source + self.epilogue)
+ self.assertRaises(exc, parse)
+
+
+def rawtext(s):
+ """Compile raw text to the appropriate instruction."""
+ if "\n" in s:
+ return ("rawtextColumn", (s, len(s) - (s.rfind("\n") + 1)))
+ else:
+ return ("rawtextOffset", (s, len(s)))
+
+
+class HTMLTALParserTestCases(TestCaseBase):
+
+ def test_code_simple_identity(self):
+ self._run_check("""<html a='b' b="c" c=d><title>My Title</html>""", [
+ rawtext('<html a="b" b="c" c="d">'
+ '<title>My Title</title></html>'),
+ ])
+
+ def test_code_implied_list_closings(self):
+ self._run_check("""<ul><li><p><p><li></ul>""", [
+ rawtext('<ul><li><p></p><p></p></li><li></li></ul>'),
+ ])
+ self._run_check("""<dl><dt><dt><dd><dd><ol><li><li></ol></dl>""", [
+ rawtext('<dl><dt></dt><dt></dt><dd></dd>'
+ '<dd><ol><li></li><li></li></ol></dd></dl>'),
+ ])
+
+ def test_code_implied_table_closings(self):
+ self._run_check("""<p>text <table><tr><th>head\t<tr><td>cell\t"""
+ """<table><tr><td>cell \n \t \n<tr>""", [
+ rawtext('<p>text</p> <table><tr><th>head</th>'
+ '</tr>\t<tr><td>cell\t<table><tr><td>cell</td>'
+ '</tr> \n \t \n<tr></tr></table></td></tr></table>'),
+ ])
+ self._run_check("""<table><tr><td>cell """
+ """<table><tr><td>cell </table></table>""", [
+ rawtext('<table><tr><td>cell <table><tr><td>cell</td></tr>'
+ ' </table></td></tr></table>'),
+ ])
+
+ def test_code_bad_nesting(self):
+ def check(self=self):
+ self._run_check("<a><b></a></b>", [])
+ self.assertRaises(htmltalparser.NestingError, check)
+
+ def test_code_attr_syntax(self):
+ output = [
+ rawtext('<a b="v" c="v" d="v" e></a>'),
+ ]
+ self._run_check("""<a b='v' c="v" d=v e>""", output)
+ self._run_check("""<a b = 'v' c = "v" d = v e>""", output)
+ self._run_check("""<a\nb\n=\n'v'\nc\n=\n"v"\nd\n=\nv\ne>""", output)
+ self._run_check("""<a\tb\t=\t'v'\tc\t=\t"v"\td\t=\tv\te>""", output)
+
+ def test_code_attr_values(self):
+ self._run_check(
+ """<a b='xxx\n\txxx' c="yyy\t\nyyy" d='\txyz\n'>""", [
+ rawtext('<a b="xxx\n\txxx" c="yyy\t\nyyy" d="\txyz\n"></a>')])
+ self._run_check("""<a b='' c="">""", [
+ rawtext('<a b="" c=""></a>'),
+ ])
+
+ def test_code_attr_entity_replacement(self):
+ # we expect entities *not* to be replaced by HTLMParser!
+ self._run_check("""<a b='&amp;&gt;&lt;&quot;&apos;'>""", [
+ rawtext('<a b="&amp;&gt;&lt;&quot;\'"></a>'),
+ ])
+ self._run_check("""<a b='\"'>""", [
+ rawtext('<a b="&quot;"></a>'),
+ ])
+ self._run_check("""<a b='&'>""", [
+ rawtext('<a b="&amp;"></a>'),
+ ])
+ self._run_check("""<a b='<'>""", [
+ rawtext('<a b="&lt;"></a>'),
+ ])
+
+ def test_code_attr_funky_names(self):
+ self._run_check("""<a a.b='v' c:d=v e-f=v>""", [
+ rawtext('<a a.b="v" c:d="v" e-f="v"></a>'),
+ ])
+
+ def test_code_pcdata_entityref(self):
+ self._run_check("""&nbsp;""", [
+ rawtext('&nbsp;'),
+ ])
+
+ def test_code_short_endtags(self):
+ self._run_check("""<html><img/></html>""", [
+ rawtext('<html><img /></html>'),
+ ])
+
+
+class METALGeneratorTestCases(TestCaseBase):
+
+ def test_null(self):
+ self._run_check("", [])
+
+ def test_define_macro(self):
+ macro = self.initial_program + [
+ ('startTag', ('p', [('metal:define-macro', 'M', 'metal')])),
+ rawtext('booh</p>'),
+ ]
+ program = [
+ ('setPosition', (1, 0)),
+ ('defineMacro', ('M', macro)),
+ ]
+ macros = {'M': macro}
+ self._run_check('<p metal:define-macro="M">booh</p>', program, macros)
+
+ def test_use_macro(self):
+ self._run_check('<p metal:use-macro="M">booh</p>', [
+ ('setPosition', (1, 0)),
+ ('useMacro',
+ ('M', '$M$', {},
+ [('startTag', ('p', [('metal:use-macro', 'M', 'metal')])),
+ rawtext('booh</p>')])),
+ ])
+
+ def test_define_slot(self):
+ macro = self.initial_program + [
+ ('startTag', ('p', [('metal:define-macro', 'M', 'metal')])),
+ rawtext('foo'),
+ ('setPosition', (1, 29)),
+ ('defineSlot', ('S',
+ [('startTag', ('span', [('metal:define-slot', 'S', 'metal')])),
+ rawtext('spam</span>')])),
+ rawtext('bar</p>'),
+ ]
+ program = [('setPosition', (1, 0)),
+ ('defineMacro', ('M', macro))]
+ macros = {'M': macro}
+ self._run_check('<p metal:define-macro="M">foo'
+ '<span metal:define-slot="S">spam</span>bar</p>',
+ program, macros)
+
+ def test_fill_slot(self):
+ self._run_check('<p metal:use-macro="M">foo'
+ '<span metal:fill-slot="S">spam</span>bar</p>', [
+ ('setPosition', (1, 0)),
+ ('useMacro',
+ ('M', '$M$',
+ {'S': [('startTag', ('span',
+ [('metal:fill-slot', 'S', 'metal')])),
+ rawtext('spam</span>')]},
+ [('startTag', ('p', [('metal:use-macro', 'M', 'metal')])),
+ rawtext('foo'),
+ ('setPosition', (1, 26)),
+ ('fillSlot', ('S',
+ [('startTag', ('span', [('metal:fill-slot', 'S', 'metal')])),
+ rawtext('spam</span>')])),
+ rawtext('bar</p>')])),
+ ])
+
+
+class TALGeneratorTestCases(TestCaseBase):
+
+ def test_null(self):
+ self._run_check("", [])
+
+ def test_define_1(self):
+ self._run_check("<p tal:define='xyzzy string:spam'></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:define': 'xyzzy string:spam'}),
+ ('setLocal', ('xyzzy', '$string:spam$')),
+ ('startTag', ('p', [('tal:define', 'xyzzy string:spam', 'tal')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_define_2(self):
+ self._run_check("<p tal:define='local xyzzy string:spam'></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:define': 'local xyzzy string:spam'}),
+ ('setLocal', ('xyzzy', '$string:spam$')),
+ ('startTag', ('p',
+ [('tal:define', 'local xyzzy string:spam', 'tal')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_define_3(self):
+ self._run_check("<p tal:define='global xyzzy string:spam'></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:define': 'global xyzzy string:spam'}),
+ ('setGlobal', ('xyzzy', '$string:spam$')),
+ ('startTag', ('p',
+ [('tal:define', 'global xyzzy string:spam', 'tal')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_define_4(self):
+ self._run_check("<p tal:define='x string:spam; y x'></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:define': 'x string:spam; y x'}),
+ ('setLocal', ('x', '$string:spam$')),
+ ('setLocal', ('y', '$x$')),
+ ('startTag', ('p', [('tal:define', 'x string:spam; y x', 'tal')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_define_5(self):
+ self._run_check("<p tal:define='x string:;;;;; y x'></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:define': 'x string:;;;;; y x'}),
+ ('setLocal', ('x', '$string:;;$')),
+ ('setLocal', ('y', '$x$')),
+ ('startTag', ('p', [('tal:define', 'x string:;;;;; y x', 'tal')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_define_6(self):
+ self._run_check(
+ "<p tal:define='x string:spam; global y x; local z y'></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope',
+ {'tal:define': 'x string:spam; global y x; local z y'}),
+ ('setLocal', ('x', '$string:spam$')),
+ ('setGlobal', ('y', '$x$')),
+ ('setLocal', ('z', '$y$')),
+ ('startTag', ('p',
+ [('tal:define', 'x string:spam; global y x; local z y', 'tal')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_condition(self):
+ self._run_check(
+ "<p><span tal:condition='python:1'><b>foo</b></span></p>", [
+ rawtext('<p>'),
+ ('setPosition', (1, 3)),
+ ('beginScope', {'tal:condition': 'python:1'}),
+ ('condition', ('$python:1$',
+ [('startTag', ('span', [('tal:condition', 'python:1', 'tal')])),
+ rawtext('<b>foo</b></span>')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_content_1(self):
+ self._run_check("<p tal:content='string:foo'>bar</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:content': 'string:foo'}),
+ ('startTag', ('p', [('tal:content', 'string:foo', 'tal')])),
+ ('insertText', ('$string:foo$', [rawtext('bar')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_content_2(self):
+ self._run_check("<p tal:content='text string:foo'>bar</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:content': 'text string:foo'}),
+ ('startTag', ('p', [('tal:content', 'text string:foo', 'tal')])),
+ ('insertText', ('$string:foo$', [rawtext('bar')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_content_3(self):
+ self._run_check("<p tal:content='structure string:<br>'>bar</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:content': 'structure string:<br>'}),
+ ('startTag', ('p',
+ [('tal:content', 'structure string:<br>', 'tal')])),
+ ('insertStructure',
+ ('$string:<br>$', {}, [rawtext('bar')])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_replace_1(self):
+ self._run_check("<p tal:replace='string:foo'>bar</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:replace': 'string:foo'}),
+ ('optTag',
+ ('p',
+ '',
+ None,
+ 0,
+ [('startTag', ('p', [('tal:replace', 'string:foo', 'tal')]))],
+ [('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])),
+ ('endScope', ()),
+ ])
+
+ def test_replace_2(self):
+ self._run_check("<p tal:replace='text string:foo'>bar</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:replace': 'text string:foo'}),
+ ('optTag',
+ ('p',
+ '',
+ None,
+ 0,
+ [('startTag', ('p', [('tal:replace', 'text string:foo', 'tal')]))],
+ [('insertText', ('$string:foo$', [('rawtextOffset', ('bar', 3))]))])),
+ ('endScope', ()),
+ ])
+
+ def test_replace_3(self):
+ self._run_check("<p tal:replace='structure string:<br>'>bar</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:replace': 'structure string:<br>'}),
+ ('optTag',
+ ('p',
+ '',
+ None,
+ 0,
+ [('startTag', ('p', [('tal:replace', 'structure string:<br>', 'tal')]))],
+ [('insertStructure',
+ ('$string:<br>$', {}, [('rawtextOffset', ('bar', 3))]))])),
+ ('endScope', ()),
+ ])
+
+ def test_repeat(self):
+ self._run_check("<p tal:repeat='x python:(1,2,3)'>"
+ "<span tal:replace='x'>dummy</span></p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:repeat': 'x python:(1,2,3)'}),
+ ('loop', ('x', '$python:(1,2,3)$',
+ [('startTag', ('p',
+ [('tal:repeat', 'x python:(1,2,3)', 'tal')])),
+ ('setPosition', (1, 33)),
+ ('beginScope', {'tal:replace': 'x'}),
+ ('optTag',
+ ('span',
+ '',
+ None,
+ 0,
+ [('startTag', ('span', [('tal:replace', 'x', 'tal')]))],
+ [('insertText', ('$x$', [('rawtextOffset', ('dummy', 5))]))])),
+ ('endScope', ()),
+ rawtext('</p>')])),
+ ('endScope', ()),
+ ])
+
+ def test_script_1(self):
+ self._run_check('<p tal:script="text/server-python">code</p>', [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'tal:script': 'text/server-python'}),
+ ('startTag', ('p',
+ [('tal:script', 'text/server-python', 'tal')])),
+ ('evaluateCode', ('text/server-python',
+ [('rawtextOffset', ('code', 4))])),
+ ('endScope', ()),
+ rawtext('</p>'),
+ ])
+
+ def test_script_2(self):
+ self._run_check('<tal:block script="text/server-python">'
+ 'code'
+ '</tal:block>', [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'script': 'text/server-python'}),
+ ('optTag',
+ ('tal:block',
+ None,
+ 'tal',
+ 0,
+ [('startTag', ('tal:block',
+ [('script', 'text/server-python', 'tal')]))],
+ [('evaluateCode',
+ ('text/server-python',
+ [('rawtextOffset', ('code', 4))]))])),
+ ('endScope', ())
+ ])
+
+ def test_script_3(self):
+ self._run_check('<script type="text/server-python">code</script>', [
+ ('setPosition', (1, 0)),
+ ('beginScope', {}),
+ ('optTag',
+ ('script',
+ '',
+ None,
+ 0,
+ [('rawtextOffset', ('<script>', 8))],
+ [('evaluateCode',
+ ('text/server-python', [('rawtextOffset', ('code', 4))]))])),
+ ('endScope', ())
+ ])
+
+ def test_script_4(self):
+ self._run_check('<script type="text/javascript">code</script>', [
+ ('rawtextOffset',
+ ('<script type="text/javascript">code</script>', 44))
+ ])
+
+ def test_attributes_1(self):
+ self._run_check("<a href='foo' name='bar' tal:attributes="
+ "'href string:http://www.zope.org; x string:y'>"
+ "link</a>", [
+ ('setPosition', (1, 0)),
+ ('beginScope',
+ {'tal:attributes': 'href string:http://www.zope.org; x string:y',
+ 'name': 'bar', 'href': 'foo'}),
+ ('startTag', ('a',
+ [('href', 'foo', 'replace', '$string:http://www.zope.org$', 0, None),
+ ('name', 'name="bar"'),
+ ('tal:attributes',
+ 'href string:http://www.zope.org; x string:y', 'tal'),
+ ('x', None, 'insert', '$string:y$', 0, None)])),
+ ('endScope', ()),
+ rawtext('link</a>'),
+ ])
+
+ def test_attributes_2(self):
+ self._run_check("<p tal:replace='structure string:<img>' "
+ "tal:attributes='src string:foo.png'>duh</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope',
+ {'tal:attributes': 'src string:foo.png',
+ 'tal:replace': 'structure string:<img>'}),
+ ('optTag',
+ ('p',
+ '',
+ None,
+ 0,
+ [('startTag',
+ ('p',
+ [('tal:replace', 'structure string:<img>', 'tal'),
+ ('tal:attributes', 'src string:foo.png', 'tal')]))],
+ [('insertStructure',
+ ('$string:<img>$',
+ {'src': ('$string:foo.png$', False, None)},
+ [('rawtextOffset', ('duh', 3))]))])),
+ ('endScope', ())])
+
+ def test_on_error_1(self):
+ self._run_check("<p tal:on-error='string:error' "
+ "tal:content='notHere'>okay</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope',
+ {'tal:content': 'notHere', 'tal:on-error': 'string:error'}),
+ ('onError',
+ ([('startTag', ('p',
+ [('tal:on-error', 'string:error', 'tal'),
+ ('tal:content', 'notHere', 'tal')])),
+ ('insertText', ('$notHere$', [rawtext('okay')])),
+ rawtext('</p>')],
+ [('startTag', ('p',
+ [('tal:on-error', 'string:error', 'tal'),
+ ('tal:content', 'notHere', 'tal')])),
+ ('insertText', ('$string:error$', [])),
+ rawtext('</p>')])),
+ ('endScope', ()),
+ ])
+
+ def test_on_error_2(self):
+ self._run_check("<p tal:on-error='string:error' "
+ "tal:replace='notHere'>okay</p>", [
+ ('setPosition', (1, 0)),
+ ('beginScope',
+ {'tal:replace': 'notHere', 'tal:on-error': 'string:error'}),
+ ('onError',
+ ([('optTag',
+ ('p',
+ '',
+ None,
+ 0,
+ [('startTag',
+ ('p',
+ [('tal:on-error', 'string:error', 'tal'),
+ ('tal:replace', 'notHere', 'tal')]))],
+ [('insertText', ('$notHere$', [('rawtextOffset', ('okay', 4))]))]))],
+ [('startTag',
+ ('p',
+ [('tal:on-error', 'string:error', 'tal'),
+ ('tal:replace', 'notHere', 'tal')])),
+ ('insertText', ('$string:error$', [])),
+ ('rawtextOffset', ('</p>', 4))])),
+ ('endScope', ()),
+ ])
+
+ def test_dup_attr(self):
+ self._should_error("<img tal:condition='x' tal:condition='x'>")
+ self._should_error("<img metal:define-macro='x' "
+ "metal:define-macro='x'>", taldefs.METALError)
+
+ def test_tal_errors(self):
+ self._should_error("<p tal:define='x' />")
+ self._should_error("<p tal:repeat='x' />")
+ self._should_error("<p tal:foobar='x' />")
+ self._should_error("<p tal:replace='x' tal:content='x' />")
+ self._should_error("<p tal:replace='x'>")
+ for tag in htmltalparser.EMPTY_HTML_TAGS:
+ self._should_error("<%s tal:content='string:foo'>" % tag)
+
+ def test_metal_errors(self):
+ exc = taldefs.METALError
+ self._should_error(2*"<p metal:define-macro='x'>xxx</p>", exc)
+ self._should_error("<html metal:use-macro='x'>" +
+ 2*"<p metal:fill-slot='y' />" + "</html>", exc)
+ self._should_error("<p metal:foobar='x' />", exc)
+ self._should_error("<p metal:define-macro='x'>", exc)
+
+ def test_extend_macro_errors(self):
+ exc = taldefs.METALError
+ # extend-macro requires define-macro:
+ self._should_error("<p metal:extend-macro='x'>xxx</p>", exc)
+ # extend-macro prevents use-macro:
+ self._should_error("<p metal:extend-macro='x'"
+ " metal:use-macro='x'"
+ " metal:define-macro='y'>xxx</p>", exc)
+ # use-macro doesn't co-exist with define-macro:
+ self._should_error("<p metal:use-macro='x'"
+ " metal:define-macro='y'>xxx</p>", exc)
+
+ #
+ # I18N test cases
+ #
+
+ def test_i18n_attributes(self):
+ self._run_check("<img alt='foo' i18n:attributes='alt'>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'alt': 'foo', 'i18n:attributes': 'alt'}),
+ ('startTag', ('img',
+ [('alt', 'foo', 'replace', None, 1, None),
+ ('i18n:attributes', 'alt', 'i18n')])),
+ ('endScope', ()),
+ ])
+ self._run_check("<img alt='foo' i18n:attributes='alt foo ; bar'>", [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'alt': 'foo', 'i18n:attributes': 'alt foo ; bar'}),
+ ('startTag', ('img',
+ [('alt', 'foo', 'replace', None, 1, 'foo'),
+ ('i18n:attributes', 'alt foo ; bar', 'i18n'),
+ ('bar', None, 'insert', None, 1, None)])),
+ ('endScope', ()),
+ ])
+
+ def test_i18n_name_bad_name(self):
+ self._should_error("<span i18n:name='not a valid name' />")
+ self._should_error("<span i18n:name='-bad-name' />")
+
+ def test_i18n_attributes_repeated_attr(self):
+ self._should_error("<a i18n:attributes='href; href' />")
+ self._should_error("<a i18n:attributes='href; HREF' />")
+
+ def test_i18n_translate(self):
+ # input/test19.html
+ self._run_check('''\
+<span i18n:translate="">Replace this</span>
+<span i18n:translate="msgid">This is a
+translated string</span>
+<span i18n:translate="">And another
+translated string</span>
+''', [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'i18n:translate': ''}),
+ ('startTag', ('span', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation', ('', [('rawtextOffset', ('Replace this', 12))])),
+ ('rawtextBeginScope',
+ ('</span>\n', 0, (2, 0), 1, {'i18n:translate': 'msgid'})),
+ ('startTag', ('span', [('i18n:translate', 'msgid', 'i18n')])),
+ ('insertTranslation',
+ ('msgid', [('rawtextColumn', ('This is a\ntranslated string', 17))])),
+ ('rawtextBeginScope', ('</span>\n', 0, (4, 0), 1, {'i18n:translate': ''})),
+ ('startTag', ('span', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation',
+ ('', [('rawtextColumn', ('And another\ntranslated string', 17))])),
+ ('endScope', ()),
+ ('rawtextColumn', ('</span>\n', 0))])
+
+ def test_i18n_translate_with_nested_tal(self):
+ self._run_check('''\
+<span i18n:translate="">replaceable <p tal:replace="str:here">content</p></span>
+''', [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'i18n:translate': ''}),
+ ('startTag', ('span', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('rawtextOffset', ('replaceable ', 12)),
+ ('setPosition', (1, 36)),
+ ('beginScope', {'tal:replace': 'str:here'}),
+ ('optTag',
+ ('p',
+ '',
+ None,
+ 0,
+ [('startTag', ('p', [('tal:replace', 'str:here', 'tal')]))],
+ [('insertText',
+ ('$str:here$', [('rawtextOffset', ('content', 7))]))])),
+ ('endScope', ())])),
+ ('endScope', ()),
+ ('rawtextColumn', ('</span>\n', 0))
+ ])
+
+ def test_i18n_name(self):
+ # input/test21.html
+ self._run_check('''\
+<span i18n:translate="">
+ <span tal:replace="str:Lomax" i18n:name="name" /> was born in
+ <span tal:replace="str:Antarctica" i18n:name="country" />.
+</span>
+''', [
+ ('setPosition', (1, 0)),
+ ('beginScope', {'i18n:translate': ''}),
+ ('startTag', ('span', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('rawtextBeginScope',
+ ('\n ',
+ 2,
+ (2, 2),
+ 0,
+ {'i18n:name': 'name', 'tal:replace': 'str:Lomax'})),
+ ('i18nVariable',
+ ('name',
+ [('optTag',
+ ('span',
+ '',
+ None,
+ 1,
+ [('startEndTag',
+ ('span',
+ [('tal:replace', 'str:Lomax', 'tal'),
+ ('i18n:name', 'name', 'i18n')]))],
+ [('insertText', ('$str:Lomax$', []))]))],
+ None,
+ 0)),
+ ('rawtextBeginScope',
+ (' was born in\n ',
+ 2,
+ (3, 2),
+ 1,
+ {'i18n:name': 'country', 'tal:replace': 'str:Antarctica'})),
+ ('i18nVariable',
+ ('country',
+ [('optTag',
+ ('span',
+ '',
+ None,
+ 1,
+ [('startEndTag',
+ ('span',
+ [('tal:replace', 'str:Antarctica', 'tal'),
+ ('i18n:name', 'country', 'i18n')]))],
+ [('insertText', ('$str:Antarctica$', []))]))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextColumn', ('.\n', 0))])),
+ ('endScope', ()),
+ ('rawtextColumn', ('</span>\n', 0))
+ ])
+
+ def test_i18n_name_with_content(self):
+ self._run_check('<div i18n:translate="">This is text for '
+ '<span i18n:translate="" tal:content="bar" i18n:name="bar_name"/>.'
+ '</div>', [
+('setPosition', (1, 0)),
+('beginScope', {'i18n:translate': ''}),
+('startTag', ('div', [('i18n:translate', '', 'i18n')])),
+('insertTranslation',
+ ('',
+ [('rawtextOffset', ('This is text for ', 17)),
+ ('setPosition', (1, 40)),
+ ('beginScope',
+ {'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
+ ('i18nVariable',
+ ('bar_name',
+ [('startTag',
+ ('span',
+ [('i18n:translate', '', 'i18n'),
+ ('tal:content', 'bar', 'tal'),
+ ('i18n:name', 'bar_name', 'i18n')])),
+ ('insertI18nText', ('$bar$', [])),
+ ('rawtextOffset', ('</span>', 7))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextOffset', ('.', 1))])),
+('endScope', ()),
+('rawtextOffset', ('</div>', 6))
+ ])
+
+ def test_i18n_name_implicit_value(self):
+ # input/test22.html
+ self._run_check('''\
+<span i18n:translate="">
+ <span tal:omit-tag="" i18n:name="name"><b>Jim</b></span> was born in
+ <span tal:omit-tag="" i18n:name="country">the USA</span>.
+</span>
+''', [('setPosition', (1, 0)),
+ ('beginScope', {'i18n:translate': ''}),
+ ('startTag', ('span', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('rawtextBeginScope',
+ ('\n ', 2, (2, 2), 0, {'i18n:name': 'name', 'tal:omit-tag': ''})),
+ ('i18nVariable',
+ ('name',
+ [('optTag',
+ ('span',
+ '',
+ None,
+ 0,
+ [('startTag',
+ ('span',
+ [('tal:omit-tag', '', 'tal'),
+ ('i18n:name', 'name', 'i18n')]))],
+ [('rawtextOffset', ('<b>Jim</b>', 10))]))],
+ None,
+ 0)),
+ ('rawtextBeginScope',
+ (' was born in\n ',
+ 2,
+ (3, 2),
+ 1,
+ {'i18n:name': 'country', 'tal:omit-tag': ''})),
+ ('i18nVariable',
+ ('country',
+ [('optTag',
+ ('span',
+ '',
+ None,
+ 0,
+ [('startTag',
+ ('span',
+ [('tal:omit-tag', '', 'tal'),
+ ('i18n:name', 'country', 'i18n')]))],
+ [('rawtextOffset', ('the USA', 7))]))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextColumn', ('.\n', 0))])),
+ ('endScope', ()),
+ ('rawtextColumn', ('</span>\n', 0))
+ ])
+
+ def test_i18n_context_domain(self):
+ self._run_check("<span i18n:domain='mydomain'/>", [
+ ('setPosition', (1, 0)),
+ ('beginI18nContext', {'domain': 'mydomain',
+ 'source': None, 'target': None}),
+ ('beginScope', {'i18n:domain': 'mydomain'}),
+ ('startEndTag', ('span', [('i18n:domain', 'mydomain', 'i18n')])),
+ ('endScope', ()),
+ ('endI18nContext', ()),
+ ])
+
+ def test_i18n_context_source(self):
+ self._run_check("<span i18n:source='en'/>", [
+ ('setPosition', (1, 0)),
+ ('beginI18nContext', {'source': 'en',
+ 'domain': 'default', 'target': None}),
+ ('beginScope', {'i18n:source': 'en'}),
+ ('startEndTag', ('span', [('i18n:source', 'en', 'i18n')])),
+ ('endScope', ()),
+ ('endI18nContext', ()),
+ ])
+
+ def test_i18n_context_source_target(self):
+ self._run_check("<span i18n:source='en' i18n:target='ru'/>", [
+ ('setPosition', (1, 0)),
+ ('beginI18nContext', {'source': 'en', 'target': 'ru',
+ 'domain': 'default'}),
+ ('beginScope', {'i18n:source': 'en', 'i18n:target': 'ru'}),
+ ('startEndTag', ('span', [('i18n:source', 'en', 'i18n'),
+ ('i18n:target', 'ru', 'i18n')])),
+ ('endScope', ()),
+ ('endI18nContext', ()),
+ ])
+
+ def test_i18n_context_in_define_slot(self):
+ text = ("<div metal:use-macro='M' i18n:domain='mydomain'>"
+ "<div metal:fill-slot='S'>spam</div>"
+ "</div>")
+ self._run_check(text, [
+ ('setPosition', (1, 0)),
+ ('useMacro',
+ ('M', '$M$',
+ {'S': [('startTag', ('div',
+ [('metal:fill-slot', 'S', 'metal')])),
+ rawtext('spam</div>')]},
+ [('beginI18nContext', {'domain': 'mydomain',
+ 'source': None, 'target': None}),
+ ('beginScope',
+ {'i18n:domain': 'mydomain', 'metal:use-macro': 'M'}),
+ ('startTag', ('div', [('metal:use-macro', 'M', 'metal'),
+ ('i18n:domain', 'mydomain', 'i18n')])),
+ ('setPosition', (1, 48)),
+ ('fillSlot', ('S',
+ [('startTag',
+ ('div', [('metal:fill-slot', 'S', 'metal')])),
+ rawtext('spam</div>')])),
+ ('endScope', ()),
+ rawtext('</div>'),
+ ('endI18nContext', ())])),
+ ])
+
+ def test_i18n_data(self):
+ # input/test23.html
+ self._run_check('''\
+<span i18n:data="here/currentTime"
+ i18n:translate="timefmt">2:32 pm</span>
+''', [
+ ('setPosition', (1, 0)),
+ ('beginScope',
+ {'i18n:translate': 'timefmt', 'i18n:data': 'here/currentTime'}),
+ ('startTag',
+ ('span',
+ [('i18n:data', 'here/currentTime', 'i18n'),
+ ('i18n:translate', 'timefmt', 'i18n')])),
+ ('insertTranslation',
+ ('timefmt', [('rawtextOffset', ('2:32 pm', 7))], '$here/currentTime$')),
+ ('endScope', ()),
+ ('rawtextColumn', ('</span>\n', 0))
+ ])
+
+ def test_i18n_data_with_name(self):
+ # input/test29.html
+ self._run_check('''\
+<div i18n:translate="">At the tone the time will be
+<span i18n:data="here/currentTime"
+ i18n:translate="timefmt"
+ i18n:name="time">2:32 pm</span>... beep!</div>
+''', [('setPosition', (1, 0)),
+ ('beginScope', {'i18n:translate': ''}),
+ ('startTag', ('div', [('i18n:translate', '', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('rawtextBeginScope',
+ ('At the tone the time will be\n',
+ 0,
+ (2, 0),
+ 0,
+ {'i18n:data': 'here/currentTime',
+ 'i18n:name': 'time',
+ 'i18n:translate': 'timefmt'})),
+ ('i18nVariable',
+ ('time',
+ [('startTag',
+ ('span',
+ [('i18n:data', 'here/currentTime', 'i18n'),
+ ('i18n:translate', 'timefmt', 'i18n'),
+ ('i18n:name', 'time', 'i18n')])),
+ ('insertTranslation',
+ ('timefmt',
+ [('rawtextOffset', ('2:32 pm', 7))],
+ '$here/currentTime$')),
+ ('rawtextOffset', ('</span>', 7))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextOffset', ('... beep!', 9))])),
+ ('endScope', ()),
+ ('rawtextColumn', ('</div>\n', 0))
+ ])
+
+ def test_i18n_name_around_tal_content(self):
+ # input/test28.html
+ self._run_check('''\
+<p i18n:translate="verify">Your contact email address is recorded as
+ <span tal:omit-tag="" i18n:name="email">
+ <a href="mailto:user@example.com"
+ tal:content="request/submitter">user@host.com</a></span>
+</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,
+ {'i18n:name': 'email', 'tal:omit-tag': ''})),
+ ('i18nVariable',
+ ('email',
+ [('optTag',
+ ('span',
+ '',
+ None,
+ 0,
+ [('startTag',
+ ('span',
+ [('tal:omit-tag', '', 'tal'),
+ ('i18n:name', 'email', 'i18n')]))],
+ [('rawtextBeginScope',
+ ('\n ',
+ 4,
+ (3, 4),
+ 0,
+ {'href': 'mailto:user@example.com',
+ 'tal:content': 'request/submitter'})),
+ ('startTag',
+ ('a',
+ [('href', 'href="mailto:user@example.com"'),
+ ('tal:content', 'request/submitter', 'tal')])),
+ ('insertText',
+ ('$request/submitter$',
+ [('rawtextOffset', ('user@host.com', 13))])),
+ ('endScope', ()),
+ ('rawtextOffset', ('</a>', 4))]))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextColumn', ('\n', 0))])),
+ ('endScope', ()),
+ ('rawtextColumn', ('</p>\n', 0))
+ ])
+
+ def test_i18n_name_with_tal_content(self):
+ # 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,
+ 0)),
+ ('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)
diff --git a/src/zope/tal/tests/test_sourcepos.py b/src/zope/tal/tests/test_sourcepos.py
new file mode 100644
index 0000000..4034b21
--- /dev/null
+++ b/src/zope/tal/tests/test_sourcepos.py
@@ -0,0 +1,93 @@
+##############################################################################
+#
+# 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.1 (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.
+
+$Id$
+"""
+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')
diff --git a/src/zope/tal/tests/test_talgettext.py b/src/zope/tal/tests/test_talgettext.py
new file mode 100644
index 0000000..bdfbfd8
--- /dev/null
+++ b/src/zope/tal/tests/test_talgettext.py
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# 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.1 (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 talgettext utility.
+
+$Id$
+"""
+import sys
+import unittest
+from StringIO import StringIO
+
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.talgettext import POTALInterpreter
+from zope.tal.talgettext import POEngine
+from zope.tal.tests import utils
+
+class test_POEngine(unittest.TestCase):
+ """Test the PO engine functionality, which simply adds items to a catalog
+ as .translate is called
+ """
+
+ def test_translate(self):
+ test_keys = ['foo', 'bar', 'blarf', 'washington']
+
+ engine = POEngine()
+ engine.file = 'foo.pt'
+ for key in test_keys:
+ engine.translate(key, 'domain')
+
+ for key in test_keys:
+ self.failIf(key not in engine.catalog['domain'],
+ "POEngine catalog does not properly store message ids"
+ )
+
+ def test_dynamic_msgids(self):
+ sample_source = """
+ <p i18n:translate="">
+ Some
+ <span tal:replace="string:strange">dynamic</span>
+ text.
+ </p>
+ <p i18n:translate="">
+ A <a tal:attributes="href path:dynamic">link</a>.
+ </p>
+ """
+ p = HTMLTALParser()
+ p.parseString(sample_source)
+ program, macros = p.getCode()
+ engine = POEngine()
+ engine.file = 'sample_source'
+ POTALInterpreter(program, macros, engine, stream=StringIO(),
+ metal=False)()
+ msgids = []
+ for domain in engine.catalog.values():
+ msgids += domain.keys()
+ msgids.sort()
+ self.assertEquals(msgids,
+ ['A <a href="${DYNAMIC_CONTENT}">link</a>.',
+ 'Some ${DYNAMIC_CONTENT} text.'])
+
+
+def test_suite():
+ suite = unittest.makeSuite(test_POEngine)
+ return suite
+
+if __name__ == "__main__":
+ errs = utils.run_suite(test_suite())
+ sys.exit(errs and 1 or 0)
diff --git a/src/zope/tal/tests/test_talinterpreter.py b/src/zope/tal/tests/test_talinterpreter.py
new file mode 100644
index 0000000..c9e8ed7
--- /dev/null
+++ b/src/zope/tal/tests/test_talinterpreter.py
@@ -0,0 +1,853 @@
+# -*- coding: ISO-8859-1 -*-
+##############################################################################
+#
+# 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.1 (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.
+
+$Id$
+"""
+import os
+import sys
+import unittest
+
+from StringIO import StringIO
+
+from zope.tal.taldefs import METALError, I18NError, TAL_VERSION
+from zope.tal.taldefs import TALExpressionError
+from zope.tal.htmltalparser import HTMLTALParser
+from zope.tal.talparser import TALParser
+from zope.tal.talinterpreter import TALInterpreter
+from zope.tal.talgenerator import TALGenerator
+from zope.tal.dummyengine import DummyEngine
+from zope.tal.dummyengine import MultipleDomainsDummyEngine
+from zope.tal.dummyengine import DummyTranslationDomain
+from zope.tal.tests import utils
+from zope.i18nmessageid import Message
+
+
+class TestCaseBase(unittest.TestCase):
+
+ def _compile(self, source, source_file=None):
+ generator = TALGenerator(xml=0, source_file=source_file)
+ parser = HTMLTALParser(generator)
+ 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 MacroFunkyErrorTest(TestCaseBase):
+
+ def test_div_in_p_using_macro(self):
+ dummy, macros = self._compile('<p metal:define-macro="M">Booh</p>')
+ engine = DummyEngine(macros)
+ program, dummy = self._compile(
+ '<p metal:use-macro="M"><div>foo</div></p>')
+ interpreter = TALInterpreter(program, {}, engine)
+
+ output = interpreter()
+ self.assertEqual(output, '<p><div>foo</div></p>')
+
+
+class MacroExtendTestCase(TestCaseBase):
+
+ def setUp(self):
+ s = self._read(('input', 'pnome_template.pt'))
+ self.pnome_program, pnome_macros = self._compile(s)
+ s = self._read(('input', 'acme_template.pt'))
+ self.acme_program, acme_macros = self._compile(s)
+ s = self._read(('input', 'document_list.pt'))
+ self.doclist_program, doclist_macros = self._compile(s)
+ macros = {
+ 'pnome_macros_page': pnome_macros['page'],
+ 'acme_macros_page': acme_macros['page'],
+ }
+ self.engine = DummyEngine(macros)
+
+ def _read(self, path):
+ dir = os.path.dirname(__file__)
+ fn = os.path.join(dir, *path)
+ f = open(fn)
+ data = f.read()
+ f.close()
+ return data
+
+ def test_preview_acme_template(self):
+ # An ACME designer is previewing the ACME design. For the
+ # purposes of this use case, extending a macro should act the
+ # same as using a macro.
+ result = StringIO()
+ interpreter = TALInterpreter(
+ self.acme_program, {}, self.engine, stream=result)
+ interpreter()
+ actual = result.getvalue().strip()
+ expected = self._read(('output', 'acme_template.html')).strip()
+ self.assertEqual(actual, expected)
+
+ def test_preview_acme_template_source(self):
+ # Render METAL attributes in acme_template
+ result = StringIO()
+ interpreter = TALInterpreter(
+ self.acme_program, {}, self.engine, stream=result, tal=False)
+ interpreter()
+ actual = result.getvalue().strip()
+ expected = self._read(('output', 'acme_template_source.html')).strip()
+ self.assertEqual(actual, expected)
+
+
+class I18NCornerTestCaseBase(TestCaseBase):
+
+ def factory(self, msgid, default, mapping={}):
+ raise NotImplementedError("abstract method")
+
+ def setUp(self):
+ self.engine = DummyEngine()
+ # Make sure we'll translate the msgid not its unicode representation
+ self.engine.setLocal('foo',
+ self.factory('FoOvAlUe${empty}', 'default', {'empty': ''}))
+ self.engine.setLocal('bar', 'BaRvAlUe')
+
+ def _check(self, program, expected):
+ result = StringIO()
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ self.assertEqual(expected, result.getvalue())
+
+ def test_simple_messageid_translate(self):
+ # This test is mainly here to make sure our DummyEngine works
+ # correctly.
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:content="foo"/>')
+ self._check(program, '<span>FOOVALUE</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:replace="foo"/>')
+ self._check(program, 'FOOVALUE\n')
+
+ # i18n messages defined in Python are translated automatically
+ # (no i18n:translate necessary)
+ program, macros = self._compile(
+ '<span tal:content="foo" />')
+ self._check(program, '<span>FOOVALUE</span>\n')
+
+ program, macros = self._compile(
+ '<span tal:replace="foo" />')
+ self._check(program, 'FOOVALUE\n')
+
+ def test_attributes_translation(self):
+ program, macros = self._compile(
+ '<span tal:attributes="test bar"/>')
+ self._check(program, '<span test="BaRvAlUe" />\n')
+
+ program, macros = self._compile(
+ '<span test="bar" i18n:attributes="test"/>')
+ self._check(program, '<span test="BAR" />\n')
+
+ program, macros = self._compile(
+ '<span tal:attributes="test bar" i18n:attributes="test"/>')
+ self._check(program, '<span test="BARVALUE" />\n')
+
+ # i18n messages defined in Python are translated automatically
+ # (no i18n:attributes necessary)
+ program, macros = self._compile(
+ '<span tal:attributes="test foo"/>')
+ self._check(program, '<span test="FOOVALUE" />\n')
+
+ def test_text_variable_translate(self):
+ program, macros = self._compile(
+ '<span tal:content="bar"/>')
+ self._check(program, '<span>BaRvAlUe</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:content="bar"/>')
+ self._check(program, '<span>BARVALUE</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:replace="bar"/>')
+ self._check(program, 'BARVALUE\n')
+
+ def test_text_translate(self):
+ program, macros = self._compile(
+ '<span tal:content="string:BaR"/>')
+ self._check(program, '<span>BaR</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:content="string:BaR"/>')
+ self._check(program, '<span>BAR</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:replace="string:BaR"/>')
+ self._check(program, 'BAR\n')
+
+ def test_structure_text_variable_translate(self):
+ program, macros = self._compile(
+ '<span tal:content="structure bar"/>')
+ self._check(program, '<span>BaRvAlUe</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:content="structure bar"/>')
+ self._check(program, '<span>BARVALUE</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:replace="structure bar"/>')
+ self._check(program, 'BARVALUE\n')
+
+ # i18n messages defined in Python are translated automatically
+ # (no i18n:translate necessary)
+ program, macros = self._compile(
+ '<span tal:content="structure foo"/>')
+ self._check(program, '<span>FOOVALUE</span>\n')
+
+ program, macros = self._compile(
+ '<span tal:replace="structure foo"/>')
+ self._check(program, 'FOOVALUE\n')
+
+ def test_structure_text_translate(self):
+ program, macros = self._compile(
+ '<span tal:content="structure string:BaR"/>')
+ self._check(program, '<span>BaR</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:content="structure string:BaR"/>')
+ self._check(program, '<span>BAR</span>\n')
+
+ program, macros = self._compile(
+ '<span i18n:translate="" tal:replace="structure string:BaR"/>')
+ self._check(program, 'BAR\n')
+
+ def test_replace_with_messageid_and_i18nname(self):
+ program, macros = self._compile(
+ '<div i18n:translate="" >'
+ '<span i18n:translate="" tal:replace="foo" i18n:name="foo_name"/>'
+ '</div>')
+ self._check(program, '<div>FOOVALUE</div>\n')
+
+ def test_pythonexpr_replace_with_messageid_and_i18nname(self):
+ program, macros = self._compile(
+ '<div i18n:translate="" >'
+ '<span i18n:translate="" tal:replace="python: foo"'
+ ' i18n:name="foo_name"/>'
+ '</div>')
+ self._check(program, '<div>FOOVALUE</div>\n')
+
+ def test_structure_replace_with_messageid_and_i18nname(self):
+ program, macros = self._compile(
+ '<div i18n:translate="" >'
+ '<span i18n:translate="" tal:replace="structure foo"'
+ ' i18n:name="foo_name"/>'
+ '</div>')
+ self._check(program, '<div>FOOVALUE</div>\n')
+
+ def test_complex_replace_with_messageid_and_i18nname(self):
+ program, macros = self._compile(
+ '<div i18n:translate="" >'
+ '<em tal:omit-tag="" i18n:name="foo_name">'
+ '<span i18n:translate="" tal:replace="foo"/>'
+ '</em>'
+ '</div>')
+ self._check(program, '<div>FOOVALUE</div>\n')
+
+ def test_content_with_messageid_and_i18nname(self):
+ program, macros = self._compile(
+ '<div i18n:translate="" >'
+ '<span i18n:translate="" tal:content="foo" i18n:name="foo_name"/>'
+ '</div>')
+ self._check(program, '<div><span>FOOVALUE</span></div>\n')
+
+ def test_content_with_messageid_and_i18nname_and_i18ntranslate(self):
+ # Let's tell the user this is incredibly silly!
+ self.assertRaises(
+ I18NError, self._compile,
+ '<span i18n:translate="" tal:content="foo" i18n:name="foo_name"/>')
+
+ def test_content_with_explicit_messageid(self):
+ # Let's tell the user this is incredibly silly!
+ self.assertRaises(
+ I18NError, self._compile,
+ '<span i18n:translate="ID" tal:content="foo" />')
+
+ def test_content_with_plaintext_and_i18nname_and_i18ntranslate(self):
+ # Let's tell the user this is incredibly silly!
+ self.assertRaises(
+ I18NError, self._compile,
+ '<span i18n:translate="" i18n:name="color_name">green</span>')
+
+ def test_translate_static_text_as_dynamic(self):
+ program, macros = self._compile(
+ '<div i18n:translate="">This is text for '
+ '<span tal:content="bar" i18n:name="bar_name"/>.'
+ '</div>')
+ self._check(program,
+ '<div>THIS IS TEXT FOR <span>BaRvAlUe</span>.</div>\n')
+ program, macros = self._compile(
+ '<div i18n:translate="">This is text for '
+ '<span i18n:translate="" tal:content="bar" i18n:name="bar_name"/>.'
+ '</div>')
+ self._check(program,
+ '<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n')
+
+ def test_translate_static_text_as_dynamic_from_bytecode(self):
+ program = [('version', TAL_VERSION),
+ ('mode', 'html'),
+('setPosition', (1, 0)),
+('beginScope', {'i18n:translate': ''}),
+('startTag', ('div', [('i18n:translate', '', 'i18n')])),
+('insertTranslation',
+ ('',
+ [('rawtextOffset', ('This is text for ', 17)),
+ ('setPosition', (1, 40)),
+ ('beginScope',
+ {'tal:content': 'bar', 'i18n:name': 'bar_name', 'i18n:translate': ''}),
+ ('i18nVariable',
+ ('bar_name',
+ [('startTag',
+ ('span',
+ [('i18n:translate', '', 'i18n'),
+ ('tal:content', 'bar', 'tal'),
+ ('i18n:name', 'bar_name', 'i18n')])),
+ ('insertTranslation',
+ ('',
+ [('insertText', ('$bar$', []))])),
+ ('rawtextOffset', ('</span>', 7))],
+ None,
+ 0)),
+ ('endScope', ()),
+ ('rawtextOffset', ('.', 1))])),
+('endScope', ()),
+('rawtextOffset', ('</div>', 6))
+]
+ self._check(program,
+ '<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n')
+
+ def test_for_correct_msgids(self):
+ self.engine.translationDomain.clearMsgids()
+ result = StringIO()
+ #GChapelle:
+ #I have the feeling the i18n:translate with the i18n:name is wrong
+ #
+ #program, macros = self._compile(
+ # '<div i18n:translate="">This is text for '
+ # '<span i18n:translate="" tal:content="bar" '
+ # 'i18n:name="bar_name"/>.</div>')
+ program, macros = self._compile(
+ '<div i18n:translate="">This is text for '
+ '<span tal:content="bar" '
+ 'i18n:name="bar_name"/>.</div>')
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ msgids = self.engine.translationDomain.getMsgids('default')
+ msgids.sort()
+ self.assertEqual(1, len(msgids))
+ self.assertEqual('This is text for ${bar_name}.', msgids[0][0])
+ self.assertEqual({'bar_name': '<span>BaRvAlUe</span>'}, msgids[0][1])
+ self.assertEqual(
+ '<div>THIS IS TEXT FOR <span>BaRvAlUe</span>.</div>\n',
+ result.getvalue())
+
+ def test_for_correct_msgids_translate_name(self):
+ self.engine.translationDomain.clearMsgids()
+ result = StringIO()
+ program, macros = self._compile(
+ '<div i18n:translate="">This is text for '
+ '<span i18n:translate="" tal:content="bar" '
+ 'i18n:name="bar_name"/>.</div>')
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ msgids = self.engine.translationDomain.getMsgids('default')
+ msgids.sort()
+ self.assertEqual(2, len(msgids))
+ self.assertEqual('This is text for ${bar_name}.', msgids[1][0])
+ self.assertEqual({'bar_name': '<span>BARVALUE</span>'}, msgids[1][1])
+ self.assertEqual(
+ '<div>THIS IS TEXT FOR <span>BARVALUE</span>.</div>\n',
+ result.getvalue())
+
+ def test_i18ntranslate_i18nname_and_attributes(self):
+ # Test for Issue 301: Bug with i18n:name and i18n:translate
+ # on the same element
+ self.engine.translationDomain.clearMsgids()
+ result = StringIO()
+ program, macros = self._compile(
+ '<p i18n:translate="">'
+ 'Some static text and a <a tal:attributes="href string:url"'
+ ' i18n:name="link" i18n:translate="">link text</a>.</p>')
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ msgids = self.engine.translationDomain.getMsgids('default')
+ msgids.sort()
+ self.assertEqual(2, len(msgids))
+ self.assertEqual('Some static text and a ${link}.', msgids[0][0])
+ self.assertEqual({'link': '<a href="url">LINK TEXT</a>'}, msgids[0][1])
+ self.assertEqual('link text', msgids[1][0])
+ self.assertEqual(
+ '<p>SOME STATIC TEXT AND A <a href="url">LINK TEXT</a>.</p>\n',
+ result.getvalue())
+
+ def test_for_raw_msgids(self):
+ # Test for Issue 314: i18n:translate removes line breaks from
+ # <pre>...</pre> contents
+ # HTML mode
+ self.engine.translationDomain.clearMsgids()
+ result = StringIO()
+ program, macros = self._compile(
+ '<div i18n:translate=""> This is text\n'
+ ' \tfor\n div. </div>'
+ '<pre i18n:translate=""> This is text\n'
+ ' <b>\tfor</b>\n pre. </pre>')
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ msgids = self.engine.translationDomain.getMsgids('default')
+ msgids.sort()
+ self.assertEqual(2, len(msgids))
+ self.assertEqual(' This is text\n <b>\tfor</b>\n pre. ', msgids[0][0])
+ self.assertEqual('This is text for div.', msgids[1][0])
+ self.assertEqual(
+ '<div>THIS IS TEXT FOR DIV.</div>'
+ '<pre> THIS IS TEXT\n <B>\tFOR</B>\n PRE. </pre>\n',
+ result.getvalue())
+
+ # XML mode
+ self.engine.translationDomain.clearMsgids()
+ result = StringIO()
+ parser = TALParser()
+ parser.parseString(
+ '<?xml version="1.0"?>\n'
+ '<pre xmlns:i18n="http://xml.zope.org/namespaces/i18n"'
+ ' i18n:translate=""> This is text\n'
+ ' <b>\tfor</b>\n barvalue. </pre>')
+ program, macros = parser.getCode()
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ msgids = self.engine.translationDomain.getMsgids('default')
+ msgids.sort()
+ self.assertEqual(1, len(msgids))
+ self.assertEqual('This is text <b> for</b> barvalue.', msgids[0][0])
+ self.assertEqual(
+ '<?xml version="1.0"?>\n'
+ '<pre>THIS IS TEXT <B> FOR</B> BARVALUE.</pre>\n',
+ result.getvalue())
+
+ def test_raw_msgids_and_i18ntranslate_i18nname(self):
+ self.engine.translationDomain.clearMsgids()
+ result = StringIO()
+ program, macros = self._compile(
+ '<div i18n:translate=""> This is text\n \tfor\n'
+ '<pre i18n:name="bar" i18n:translate=""> \tbar\n </pre>.</div>')
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ msgids = self.engine.translationDomain.getMsgids('default')
+ msgids.sort()
+ self.assertEqual(2, len(msgids))
+ self.assertEqual(' \tbar\n ', msgids[0][0])
+ self.assertEqual('This is text for ${bar}.', msgids[1][0])
+ self.assertEqual({'bar': '<pre> \tBAR\n </pre>'}, msgids[1][1])
+ self.assertEqual(
+ u'<div>THIS IS TEXT FOR <pre> \tBAR\n </pre>.</div>\n',
+ result.getvalue())
+
+ def test_for_handling_unicode_vars(self):
+ # Make sure that non-ASCII Unicode is substituted correctly.
+ # http://collector.zope.org/Zope3-dev/264
+ program, macros = self._compile(
+ "<div i18n:translate='' tal:define='bar python:unichr(0xC0)'>"
+ "Foo <span tal:replace='bar' i18n:name='bar' /></div>")
+ self._check(program, u"<div>FOO \u00C0</div>\n")
+
+class I18NCornerTestCaseMessage(I18NCornerTestCaseBase):
+
+ def factory(self, msgid, default=None, mapping={}, domain=None):
+ return Message(msgid, domain=domain, default=default, mapping=mapping)
+
+class UnusedExplicitDomainTestCase(I18NCornerTestCaseMessage):
+
+ def setUp(self):
+ # MultipleDomainsDummyEngine is a Engine
+ # where default domain transforms to uppercase
+ self.engine = MultipleDomainsDummyEngine()
+ self.engine.setLocal('foo',
+ self.factory('FoOvAlUe${empty}', 'default', {'empty': ''}))
+ self.engine.setLocal('bar', 'BaRvAlUe')
+ self.engine.setLocal('baz',
+ self.factory('BaZvAlUe', 'default', {}))
+ # Message ids with different domains
+ self.engine.setLocal('toupper',
+ self.factory('ToUpper', 'default', {}))
+ self.engine.setLocal('tolower',
+ self.factory('ToLower', 'default', {}, domain='lower'))
+
+ def test_multiple_domains(self):
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' tal:content="toupper" />')
+ self._check(program, '<div>TOUPPER</div>\n')
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' tal:content="tolower" />')
+ self._check(program, '<div>tolower</div>\n')
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' tal:content="string:ToUpper" />')
+ self._check(program, '<div>TOUPPER</div>\n')
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' i18n:domain="lower"'
+ ' tal:content="string:ToLower" />')
+ self._check(program, '<div>tolower</div>\n')
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' tal:define="msgid string:ToUpper"'
+ ' tal:content="msgid" />')
+ self._check(program, '<div>TOUPPER</div>\n')
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' i18n:domain="lower"'
+ ' tal:define="msgid string:ToLower"'
+ ' tal:content="msgid" />')
+ self._check(program, '<div>tolower</div>\n')
+
+ def test_unused_explicit_domain(self):
+ #a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine
+ #is a domain that transforms to lowercase
+ self.engine.setLocal('othertolower',
+ self.factory('OtherToLower', 'a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine', {}, domain='lower'))
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' tal:content="othertolower" />')
+ self._check(program, '<div>othertolower</div>\n')
+ #takes domain into account for strings
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' i18n:domain="a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine"'
+ ' tal:content="string:ToLower" />')
+ self._check(program, '<div>tolower</div>\n')
+ #but not for messageids
+ program, macros = self._compile(
+ '<div i18n:translate=""'
+ ' i18n:domain="a_very_explicit_domain_setup_by_template_developer_that_wont_be_taken_into_account_by_the_ZPT_engine"'
+ ' tal:content="baz" />')
+ self._check(program, '<div>BAZVALUE</div>\n')
+
+class ScriptTestCase(TestCaseBase):
+
+ def setUp(self):
+ self.engine = DummyEngine()
+
+ def _check(self, program, expected):
+ result = StringIO()
+ self.interpreter = TALInterpreter(program, {}, self.engine,
+ stream=result)
+ self.interpreter()
+ self.assertEqual(expected, result.getvalue())
+
+ def test_simple(self):
+ program, macros = self._compile(
+ '<p tal:script="text/server-python">print "hello"</p>')
+ self._check(program, '<p>hello\n</p>\n')
+
+ def test_script_and_tal_block(self):
+ program, macros = self._compile(
+ '<tal:block script="text/server-python">\n'
+ ' global x\n'
+ ' x = 1\n'
+ '</tal:block>\n'
+ '<span tal:replace="x" />')
+ self._check(program, '\n1\n')
+ self.assertEqual(self.engine.codeGlobals['x'], 1)
+
+ def test_script_and_tal_block_having_inside_print(self):
+ program, macros = self._compile(
+ '<tal:block script="text/server-python">\n'
+ ' print "hello"'
+ '</tal:block>')
+ self._check(program, 'hello\n\n')
+
+ def test_script_and_omittag(self):
+ program, macros = self._compile(
+ '<p tal:omit-tag="" tal:script="text/server-python">\n'
+ ' print "hello"'
+ '</p>')
+ self._check(program, 'hello\n\n')
+
+ def test_script_and_inside_tags(self):
+ program, macros = self._compile(
+ '<p tal:omit-tag="" tal:script="text/server-python">\n'
+ ' print "<b>hello</b>"'
+ '</p>')
+ self._check(program, '<b>hello</b>\n\n')
+
+ def test_script_and_inside_tags_with_tal(self):
+ program, macros = self._compile(
+ '<p tal:omit-tag="" tal:script="text/server-python"> <!--\n'
+ ' print """<b tal:replace="string:foo">hello</b>"""\n'
+ '--></p>')
+ self._check(program, '<b tal:replace="string:foo">hello</b>\n\n')
+
+ def test_html_script(self):
+ program, macros = self._compile(
+ '<script type="text/server-python">\n'
+ ' print "Hello world!"\n'
+ '</script>')
+ self._check(program, 'Hello world!\n')
+
+ def test_html_script_and_javascript(self):
+ program, macros = self._compile(
+ '<script type="text/javascript" src="somefile.js" />\n'
+ '<script type="text/server-python">\n'
+ ' print "Hello world!"\n'
+ '</script>')
+ self._check(program,
+ '<script type="text/javascript" src="somefile.js" />\n'
+ 'Hello world!\n')
+
+
+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")
+
+ def test_id_with_attributes(self):
+ self._check('''<input name="Delete"
+ tal:attributes="name string:delete_button"
+ i18n:attributes="name message-id">''',
+ "expected attribute being both part of tal:attributes" +
+ " and having a msgid in i18n:attributes to be denied")
+
+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_unicode_content(self):
+ INPUT = """<p tal:content="python:u'déjà-vu'">para</p>"""
+ EXPECTED = u"""<p>déjà-vu</p>""" "\n"
+ self.compare(INPUT, EXPECTED)
+
+ def test_unicode_structure(self):
+ INPUT = """<p tal:replace="structure python:u'déjà-vu'">para</p>"""
+ EXPECTED = u"""déjà-vu""" "\n"
+ self.compare(INPUT, EXPECTED)
+
+ def test_i18n_replace_number(self):
+ INPUT = """
+ <p i18n:translate="foo ${bar}">
+ <span tal:replace="python:123" i18n:name="bar">para</span>
+ </p>"""
+ EXPECTED = u"""
+ <p>FOO 123</p>""" "\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)
+
+
+class TestSourceAnnotations(unittest.TestCase):
+
+ # there are additional test files in input/ and output/ subdirs
+ # (test_sa*)
+
+ def setUp(self):
+ program = []
+ macros = {}
+ engine = DummyEngine()
+ self.interpreter = TALInterpreter(program, macros, engine)
+ self.sio = self.interpreter.stream = StringIO()
+ self.interpreter._pending_source_annotation = True
+
+ def testFormatSourceAnnotation(self):
+ interpreter = self.interpreter
+ interpreter.sourceFile = '/path/to/source.pt'
+ interpreter.position = (123, 42)
+ self.assertEquals(interpreter.formatSourceAnnotation(),
+ "<!--\n" +
+ "=" * 78 + "\n" +
+ "/path/to/source.pt (line 123)\n" +
+ "=" * 78 + "\n" +
+ "-->")
+
+ def testFormatSourceAnnotation_no_position(self):
+ interpreter = self.interpreter
+ interpreter.sourceFile = '/path/to/source.pt'
+ interpreter.position = (None, None)
+ self.assertEquals(interpreter.formatSourceAnnotation(),
+ "<!--\n" +
+ "=" * 78 + "\n" +
+ "/path/to/source.pt\n" +
+ "=" * 78 + "\n" +
+ "-->")
+
+ def test_annotated_stream_write(self):
+ interpreter = self.interpreter
+ interpreter.formatSourceAnnotation = lambda: '@'
+ test_cases = [
+ '@some text',
+ '\n',
+ '<?xml ...?>@some text',
+ ' <?xml ...?>@some text',
+ '\n<?xml ...?>@some text',
+ '<?xml ...',
+ '<?xml ...?>@\n<!DOCTYPE ...>some text',
+ ]
+ for output in test_cases:
+ input = output.replace('@', '')
+ self.sio.seek(0)
+ self.sio.truncate()
+ interpreter._pending_source_annotation = True
+ interpreter._annotated_stream_write(input)
+ self.assertEquals(self.sio.getvalue(), output)
+ if '@' in output:
+ self.assert_(not interpreter._pending_source_annotation)
+ else:
+ self.assert_(interpreter._pending_source_annotation)
+
+
+class TestErrorTracebacks(TestCaseBase):
+
+ # Regression test for http://www.zope.org/Collectors/Zope3-dev/697
+
+ def test_define_slot_does_not_clobber_source_file_on_exception(self):
+ m_program, m_macros = self._compile("""
+ <div metal:define-macro="amacro">
+ <div metal:define-slot="aslot">
+ </div>
+ </div>
+ """, source_file='macros.pt')
+ p_program, p_macros = self._compile("""
+ <div metal:use-macro="amacro">
+ <div metal:fill-slot="aslot">
+ <tal:x replace="no_such_thing" />
+ </div>
+ </div>
+ """, source_file='page.pt')
+ engine = DummyEngine(macros=m_macros)
+ interp = TALInterpreter(p_program, {}, engine, StringIO())
+ # Expect TALExpressionError: unknown variable: 'no_such_thing'
+ self.assertRaises(TALExpressionError, interp)
+ # Now the engine should know where the error occurred
+ self.assertEquals(engine.source_file, 'page.pt')
+ self.assertEquals(engine.position, (4, 16))
+
+ def test_define_slot_restores_source_file_if_no_exception(self):
+ m_program, m_macros = self._compile("""
+ <div metal:define-macro="amacro">
+ <div metal:define-slot="aslot">
+ </div>
+ <tal:x replace="no_such_thing" />
+ </div>
+ """, source_file='macros.pt')
+ p_program, p_macros = self._compile("""
+ <div metal:use-macro="amacro">
+ <div metal:fill-slot="aslot">
+ </div>
+ </div>
+ """, source_file='page.pt')
+ engine = DummyEngine(macros=m_macros)
+ interp = TALInterpreter(p_program, {}, engine, StringIO())
+ # Expect TALExpressionError: unknown variable: 'no_such_thing'
+ self.assertRaises(TALExpressionError, interp)
+ # Now the engine should know where the error occurred
+ self.assertEquals(engine.source_file, 'macros.pt')
+ self.assertEquals(engine.position, (5, 14))
+
+
+
+def test_suite():
+ suite = unittest.makeSuite(I18NErrorsTestCase)
+ suite.addTest(unittest.makeSuite(MacroErrorsTestCase))
+ suite.addTest(unittest.makeSuite(MacroExtendTestCase))
+ suite.addTest(unittest.makeSuite(OutputPresentationTestCase))
+ suite.addTest(unittest.makeSuite(ScriptTestCase))
+ suite.addTest(unittest.makeSuite(I18NCornerTestCaseMessage))
+ suite.addTest(unittest.makeSuite(UnusedExplicitDomainTestCase))
+ suite.addTest(unittest.makeSuite(TestSourceAnnotations))
+ suite.addTest(unittest.makeSuite(TestErrorTracebacks))
+
+ # TODO: Deactivated test, since we have not found a solution for this and
+ # it is a deep and undocumented HTML parser issue.
+ # Fred is looking into this.
+ #suite.addTest(unittest.makeSuite(MacroFunkyErrorTest))
+
+ return suite
+
+if __name__ == "__main__":
+ errs = utils.run_suite(test_suite())
+ sys.exit(errs and 1 or 0)
diff --git a/src/zope/tal/tests/test_talparser.py b/src/zope/tal/tests/test_talparser.py
new file mode 100644
index 0000000..f159cc1
--- /dev/null
+++ b/src/zope/tal/tests/test_talparser.py
@@ -0,0 +1,39 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 zope.tal.talparser.
+
+$Id$
+"""
+import unittest
+
+from zope.tal import talparser
+
+
+class TALParserTestCase(unittest.TestCase):
+
+ def test_parser_returns_macros(self):
+ parser = talparser.TALParser()
+ parser.parseString(
+ "<?xml version='1.0'?>\n"
+ "<doc xmlns:metal='http://xml.zope.org/namespaces/metal'>\n"
+ " <m metal:define-macro='MACRO'>\n"
+ " <para>some text</para>\n"
+ " </m>\n"
+ "</doc>")
+ bytecode, macros = parser.getCode()
+ self.assertEqual(macros.keys(), ["MACRO"])
+
+
+def test_suite():
+ return unittest.makeSuite(TALParserTestCase)
diff --git a/src/zope/tal/tests/test_xmlparser.py b/src/zope/tal/tests/test_xmlparser.py
new file mode 100644
index 0000000..02d5848
--- /dev/null
+++ b/src/zope/tal/tests/test_xmlparser.py
@@ -0,0 +1,268 @@
+##############################################################################
+#
+# 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.1 (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.
+
+$Id$
+"""
+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(object):
+ 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' e-f='v'/>""", [
+ ("starttag", "a", ["a.b", "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 $ >")
+
+ def test_unicode_string(self):
+ output = [('starttag', u'p', []),
+ ('data', u'\xe4\xf6\xfc\xdf'),
+ ('endtag', u'p')]
+ self._run_check(u'<p>\xe4\xf6\xfc\xdf</p>', output)
+
+
+# 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)
diff --git a/src/zope/tal/tests/utils.py b/src/zope/tal/tests/utils.py
new file mode 100644
index 0000000..03eba52
--- /dev/null
+++ b/src/zope/tal/tests/utils.py
@@ -0,0 +1,65 @@
+##############################################################################
+#
+# 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.1 (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.
+
+$Id$
+"""
+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