1
2
3 """
4 Test cases related to DTD parsing and validation
5 """
6
7 import unittest, sys, os.path
8
9 this_dir = os.path.dirname(__file__)
10 if this_dir not in sys.path:
11 sys.path.insert(0, this_dir)
12
13 from common_imports import etree, StringIO, BytesIO, _bytes, doctest
14 from common_imports import HelperTestCase, fileInTestDir, make_doctest, skipIf
19
27
32
39
55
60
66
71
76
88
101
103 root = etree.XML(_bytes('''
104 <!DOCTYPE b SYSTEM "none" [
105 <!ATTLIST a
106 attr1 (x | y | z) "z"
107 attr2 CDATA #FIXED "X"
108 >
109 <!ELEMENT b (a)>
110 <!ELEMENT a EMPTY>
111 ]>
112 <b><a/></b>
113 '''))
114 dtd = etree.ElementTree(root).docinfo.internalDTD
115 self.assertTrue(dtd)
116 dtd.assertValid(root)
117
118 seen = []
119 for el in dtd.iterelements():
120 if el.name == 'a':
121 self.assertEqual(2, len(el.attributes()))
122 for attr in el.iterattributes():
123 if attr.name == 'attr1':
124 self.assertEqual('enumeration', attr.type)
125 self.assertEqual('none', attr.default)
126 self.assertEqual('z', attr.default_value)
127 values = attr.values()
128 values.sort()
129 self.assertEqual(['x', 'y', 'z'], values)
130 else:
131 self.assertEqual('attr2', attr.name)
132 self.assertEqual('cdata', attr.type)
133 self.assertEqual('fixed', attr.default)
134 self.assertEqual('X', attr.default_value)
135 else:
136 self.assertEqual('b', el.name)
137 self.assertEqual(0, len(el.attributes()))
138 seen.append(el.name)
139 seen.sort()
140 self.assertEqual(['a', 'b'], seen)
141 self.assertEqual(2, len(dtd.elements()))
142
144 for el_count in range(2, 5):
145 for attr_count in range(4):
146 root = etree.XML(_bytes('''
147 <!DOCTYPE el0 SYSTEM "none" [
148 ''' + ''.join(['''
149 <!ATTLIST el%d
150 attr%d (x | y | z) "z"
151 >
152 ''' % (e, a) for a in range(attr_count) for e in range(el_count)
153 ]) + ''.join(['''
154 <!ELEMENT el%d EMPTY>
155 ''' % e for e in range(1, el_count)
156 ]) + '''
157 ''' + '<!ELEMENT el0 (%s)>' % '|'.join([
158 'el%d' % e for e in range(1, el_count)]) + '''
159 ]>
160 <el0><el1 %s /></el0>
161 ''' % ' '.join(['attr%d="x"' % a for a in range(attr_count)])))
162 dtd = etree.ElementTree(root).docinfo.internalDTD
163 self.assertTrue(dtd)
164 dtd.assertValid(root)
165
166 e = -1
167 for e, el in enumerate(dtd.iterelements()):
168 self.assertEqual(attr_count, len(el.attributes()))
169 a = -1
170 for a, attr in enumerate(el.iterattributes()):
171 self.assertEqual('enumeration', attr.type)
172 self.assertEqual('none', attr.default)
173 self.assertEqual('z', attr.default_value)
174 values = sorted(attr.values())
175 self.assertEqual(['x', 'y', 'z'], values)
176 self.assertEqual(attr_count - 1, a)
177 self.assertEqual(el_count - 1, e)
178 self.assertEqual(el_count, len(dtd.elements()))
179
183
196
197 @skipIf(etree.LIBXML_VERSION == (2, 9, 0),
198 "DTD loading is broken for incremental parsing in libxml2 2.9.0")
200 iterparse = etree.iterparse
201 iterator = iterparse(fileInTestDir("test.xml"), events=('start',),
202 attribute_defaults=True)
203 attributes = [ element.get("default")
204 for event, element in iterator ]
205 self.assertEqual(
206 ["valueA", "valueB"],
207 attributes)
208
209 @skipIf(etree.LIBXML_VERSION == (2, 9, 0),
210 "DTD loading is broken for incremental parsing in libxml2 2.9.0")
212 iterparse = etree.iterparse
213 iterator = iterparse(fileInTestDir("test.xml"), events=('end',),
214 attribute_defaults=True)
215 attributes = [ element.get("default")
216 for event, element in iterator ]
217 self.assertEqual(
218 ["valueB", "valueA"],
219 attributes)
220
222 dtd = etree.DTD(fileInTestDir("test.dtd"))
223
224
225 self.assertTrue(dtd.system_url.endswith("test.dtd"))
226
227
228 a = dtd.elements()[0]
229 self.assertEqual(a.name, "a")
230 self.assertEqual(a.type, "element")
231 self.assertEqual(a.content.name, "b")
232 self.assertEqual(a.content.type, "element")
233 self.assertEqual(a.content.occur, "once")
234
235 aattr = a.attributes()[0]
236 self.assertEqual(aattr.name, "default")
237 self.assertEqual(aattr.type, "enumeration")
238 self.assertEqual(aattr.values(), ["valueA", "valueB"])
239 self.assertEqual(aattr.default_value, "valueA")
240
241 b = dtd.elements()[1]
242 self.assertEqual(b.name, "b")
243 self.assertEqual(b.type, "empty")
244 self.assertEqual(b.content, None)
245
246
247 c = dtd.entities()[0]
248 self.assertEqual(c.name, "c")
249 self.assertEqual(c.orig, "*")
250 self.assertEqual(c.content, "*")
251
252
253 root = etree.XML(_bytes('''
254 <!DOCTYPE a SYSTEM "none" [
255 <!ELEMENT a EMPTY>
256 ]>
257 <a/>
258 '''))
259 dtd = etree.ElementTree(root).docinfo.internalDTD
260 self.assertEqual(dtd.name, "a")
261
262
263 parser = etree.XMLParser(dtd_validation=True)
264 xml = '<!DOCTYPE a SYSTEM "test.dtd"><a><b/></a>'
265 root = etree.fromstring(xml, parser=parser,
266 base_url=fileInTestDir("test.xml"))
267
268 dtd = root.getroottree().docinfo.internalDTD
269 self.assertEqual(dtd.name, "a")
270 self.assertEqual(dtd.system_url, "test.dtd")
271
274 suite = unittest.TestSuite()
275 suite.addTests([unittest.makeSuite(ETreeDtdTestCase)])
276 suite.addTests(
277 [make_doctest('../../../doc/validation.txt')])
278 return suite
279
280 if __name__ == '__main__':
281 print('to test use test.py %s' % __file__)
282