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, BytesIO, _bytes
14 from common_imports import HelperTestCase, make_doctest, skipIf
15 from common_imports import fileInTestDir, fileUrlInTestDir
21
29
34
42
60
66
72
78
84
89
94
106
119
135
137 root = etree.XML(_bytes('''
138 <!DOCTYPE b SYSTEM "none" [
139 <!ATTLIST a
140 attr1 (x | y | z) "z"
141 attr2 CDATA #FIXED "X"
142 >
143 <!ELEMENT b (a)>
144 <!ELEMENT a EMPTY>
145 ]>
146 <b><a/></b>
147 '''))
148 dtd = etree.ElementTree(root).docinfo.internalDTD
149 self.assertTrue(dtd)
150 dtd.assertValid(root)
151
152 seen = []
153 for el in dtd.iterelements():
154 if el.name == 'a':
155 self.assertEqual(2, len(el.attributes()))
156 for attr in el.iterattributes():
157 if attr.name == 'attr1':
158 self.assertEqual('enumeration', attr.type)
159 self.assertEqual('none', attr.default)
160 self.assertEqual('z', attr.default_value)
161 values = attr.values()
162 values.sort()
163 self.assertEqual(['x', 'y', 'z'], values)
164 else:
165 self.assertEqual('attr2', attr.name)
166 self.assertEqual('cdata', attr.type)
167 self.assertEqual('fixed', attr.default)
168 self.assertEqual('X', attr.default_value)
169 else:
170 self.assertEqual('b', el.name)
171 self.assertEqual(0, len(el.attributes()))
172 seen.append(el.name)
173 seen.sort()
174 self.assertEqual(['a', 'b'], seen)
175 self.assertEqual(2, len(dtd.elements()))
176
178 for el_count in range(2, 5):
179 for attr_count in range(4):
180 root = etree.XML(_bytes('''
181 <!DOCTYPE el0 SYSTEM "none" [
182 ''' + ''.join(['''
183 <!ATTLIST el%d
184 attr%d (x | y | z) "z"
185 >
186 ''' % (e, a) for a in range(attr_count) for e in range(el_count)
187 ]) + ''.join(['''
188 <!ELEMENT el%d EMPTY>
189 ''' % e for e in range(1, el_count)
190 ]) + '''
191 ''' + '<!ELEMENT el0 (%s)>' % '|'.join([
192 'el%d' % e for e in range(1, el_count)]) + '''
193 ]>
194 <el0><el1 %s /></el0>
195 ''' % ' '.join(['attr%d="x"' % a for a in range(attr_count)])))
196 dtd = etree.ElementTree(root).docinfo.internalDTD
197 self.assertTrue(dtd)
198 dtd.assertValid(root)
199
200 e = -1
201 for e, el in enumerate(dtd.iterelements()):
202 self.assertEqual(attr_count, len(el.attributes()))
203 a = -1
204 for a, attr in enumerate(el.iterattributes()):
205 self.assertEqual('enumeration', attr.type)
206 self.assertEqual('none', attr.default)
207 self.assertEqual('z', attr.default_value)
208 values = sorted(attr.values())
209 self.assertEqual(['x', 'y', 'z'], values)
210 self.assertEqual(attr_count - 1, a)
211 self.assertEqual(el_count - 1, e)
212 self.assertEqual(el_count, len(dtd.elements()))
213
217
230
231 @skipIf(etree.LIBXML_VERSION == (2, 9, 0),
232 "DTD loading is broken for incremental parsing in libxml2 2.9.0")
234 iterparse = etree.iterparse
235 iterator = iterparse(fileInTestDir("test.xml"), events=('start',),
236 attribute_defaults=True)
237 attributes = [ element.get("default")
238 for event, element in iterator ]
239 self.assertEqual(
240 ["valueA", "valueB"],
241 attributes)
242
243 @skipIf(etree.LIBXML_VERSION == (2, 9, 0),
244 "DTD loading is broken for incremental parsing in libxml2 2.9.0")
246 iterparse = etree.iterparse
247 iterator = iterparse(fileInTestDir("test.xml"), events=('end',),
248 attribute_defaults=True)
249 attributes = [ element.get("default")
250 for event, element in iterator ]
251 self.assertEqual(
252 ["valueB", "valueA"],
253 attributes)
254
256 dtd = etree.DTD(fileUrlInTestDir("test.dtd"))
257
258
259 self.assertTrue(dtd.system_url.endswith("test.dtd"))
260
261
262 a = dtd.elements()[0]
263 self.assertEqual(a.name, "a")
264 self.assertEqual(a.type, "element")
265 self.assertEqual(a.content.name, "b")
266 self.assertEqual(a.content.type, "element")
267 self.assertEqual(a.content.occur, "once")
268
269 aattr = a.attributes()[0]
270 self.assertEqual(aattr.name, "default")
271 self.assertEqual(aattr.type, "enumeration")
272 self.assertEqual(aattr.values(), ["valueA", "valueB"])
273 self.assertEqual(aattr.default_value, "valueA")
274
275 b = dtd.elements()[1]
276 self.assertEqual(b.name, "b")
277 self.assertEqual(b.type, "empty")
278 self.assertEqual(b.content, None)
279
280
281 c = dtd.entities()[0]
282 self.assertEqual(c.name, "c")
283 self.assertEqual(c.orig, "*")
284 self.assertEqual(c.content, "*")
285
286
287 root = etree.XML(_bytes('''
288 <!DOCTYPE a SYSTEM "none" [
289 <!ELEMENT a EMPTY>
290 ]>
291 <a/>
292 '''))
293 dtd = etree.ElementTree(root).docinfo.internalDTD
294 self.assertEqual(dtd.name, "a")
295
296
297 parser = etree.XMLParser(dtd_validation=True)
298 xml = '<!DOCTYPE a SYSTEM "test.dtd"><a><b/></a>'
299 root = etree.fromstring(xml, parser=parser,
300 base_url=fileUrlInTestDir("test.xml"))
301
302 dtd = root.getroottree().docinfo.internalDTD
303 self.assertEqual(dtd.name, "a")
304 self.assertEqual(dtd.system_url, "test.dtd")
305
316
323
330
333 suite = unittest.TestSuite()
334 suite.addTests([unittest.makeSuite(ETreeDtdTestCase)])
335 suite.addTests(
336 [make_doctest('../../../doc/validation.txt')])
337 return suite
338
339 if __name__ == '__main__':
340 print('to test use test.py %s' % __file__)
341