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, make_doctest, skipIf
15 from common_imports import fileInTestDir, fileUrlInTestDir
20
28
33
40
58
63
69
75
81
86
91
103
116
118 root = etree.XML(_bytes('''
119 <!DOCTYPE b SYSTEM "none" [
120 <!ATTLIST a
121 attr1 (x | y | z) "z"
122 attr2 CDATA #FIXED "X"
123 >
124 <!ELEMENT b (a)>
125 <!ELEMENT a EMPTY>
126 ]>
127 <b><a/></b>
128 '''))
129 dtd = etree.ElementTree(root).docinfo.internalDTD
130 self.assertTrue(dtd)
131 dtd.assertValid(root)
132
133 seen = []
134 for el in dtd.iterelements():
135 if el.name == 'a':
136 self.assertEqual(2, len(el.attributes()))
137 for attr in el.iterattributes():
138 if attr.name == 'attr1':
139 self.assertEqual('enumeration', attr.type)
140 self.assertEqual('none', attr.default)
141 self.assertEqual('z', attr.default_value)
142 values = attr.values()
143 values.sort()
144 self.assertEqual(['x', 'y', 'z'], values)
145 else:
146 self.assertEqual('attr2', attr.name)
147 self.assertEqual('cdata', attr.type)
148 self.assertEqual('fixed', attr.default)
149 self.assertEqual('X', attr.default_value)
150 else:
151 self.assertEqual('b', el.name)
152 self.assertEqual(0, len(el.attributes()))
153 seen.append(el.name)
154 seen.sort()
155 self.assertEqual(['a', 'b'], seen)
156 self.assertEqual(2, len(dtd.elements()))
157
159 for el_count in range(2, 5):
160 for attr_count in range(4):
161 root = etree.XML(_bytes('''
162 <!DOCTYPE el0 SYSTEM "none" [
163 ''' + ''.join(['''
164 <!ATTLIST el%d
165 attr%d (x | y | z) "z"
166 >
167 ''' % (e, a) for a in range(attr_count) for e in range(el_count)
168 ]) + ''.join(['''
169 <!ELEMENT el%d EMPTY>
170 ''' % e for e in range(1, el_count)
171 ]) + '''
172 ''' + '<!ELEMENT el0 (%s)>' % '|'.join([
173 'el%d' % e for e in range(1, el_count)]) + '''
174 ]>
175 <el0><el1 %s /></el0>
176 ''' % ' '.join(['attr%d="x"' % a for a in range(attr_count)])))
177 dtd = etree.ElementTree(root).docinfo.internalDTD
178 self.assertTrue(dtd)
179 dtd.assertValid(root)
180
181 e = -1
182 for e, el in enumerate(dtd.iterelements()):
183 self.assertEqual(attr_count, len(el.attributes()))
184 a = -1
185 for a, attr in enumerate(el.iterattributes()):
186 self.assertEqual('enumeration', attr.type)
187 self.assertEqual('none', attr.default)
188 self.assertEqual('z', attr.default_value)
189 values = sorted(attr.values())
190 self.assertEqual(['x', 'y', 'z'], values)
191 self.assertEqual(attr_count - 1, a)
192 self.assertEqual(el_count - 1, e)
193 self.assertEqual(el_count, len(dtd.elements()))
194
198
211
212 @skipIf(etree.LIBXML_VERSION == (2, 9, 0),
213 "DTD loading is broken for incremental parsing in libxml2 2.9.0")
215 iterparse = etree.iterparse
216 iterator = iterparse(fileInTestDir("test.xml"), events=('start',),
217 attribute_defaults=True)
218 attributes = [ element.get("default")
219 for event, element in iterator ]
220 self.assertEqual(
221 ["valueA", "valueB"],
222 attributes)
223
224 @skipIf(etree.LIBXML_VERSION == (2, 9, 0),
225 "DTD loading is broken for incremental parsing in libxml2 2.9.0")
227 iterparse = etree.iterparse
228 iterator = iterparse(fileInTestDir("test.xml"), events=('end',),
229 attribute_defaults=True)
230 attributes = [ element.get("default")
231 for event, element in iterator ]
232 self.assertEqual(
233 ["valueB", "valueA"],
234 attributes)
235
237 dtd = etree.DTD(fileInTestDir("test.dtd"))
238
239
240 self.assertTrue(dtd.system_url.endswith("test.dtd"))
241
242
243 a = dtd.elements()[0]
244 self.assertEqual(a.name, "a")
245 self.assertEqual(a.type, "element")
246 self.assertEqual(a.content.name, "b")
247 self.assertEqual(a.content.type, "element")
248 self.assertEqual(a.content.occur, "once")
249
250 aattr = a.attributes()[0]
251 self.assertEqual(aattr.name, "default")
252 self.assertEqual(aattr.type, "enumeration")
253 self.assertEqual(aattr.values(), ["valueA", "valueB"])
254 self.assertEqual(aattr.default_value, "valueA")
255
256 b = dtd.elements()[1]
257 self.assertEqual(b.name, "b")
258 self.assertEqual(b.type, "empty")
259 self.assertEqual(b.content, None)
260
261
262 c = dtd.entities()[0]
263 self.assertEqual(c.name, "c")
264 self.assertEqual(c.orig, "*")
265 self.assertEqual(c.content, "*")
266
267
268 root = etree.XML(_bytes('''
269 <!DOCTYPE a SYSTEM "none" [
270 <!ELEMENT a EMPTY>
271 ]>
272 <a/>
273 '''))
274 dtd = etree.ElementTree(root).docinfo.internalDTD
275 self.assertEqual(dtd.name, "a")
276
277
278 parser = etree.XMLParser(dtd_validation=True)
279 xml = '<!DOCTYPE a SYSTEM "test.dtd"><a><b/></a>'
280 root = etree.fromstring(xml, parser=parser,
281 base_url=fileInTestDir("test.xml"))
282
283 dtd = root.getroottree().docinfo.internalDTD
284 self.assertEqual(dtd.name, "a")
285 self.assertEqual(dtd.system_url, "test.dtd")
286
289 suite = unittest.TestSuite()
290 suite.addTests([unittest.makeSuite(ETreeDtdTestCase)])
291 suite.addTests(
292 [make_doctest('../../../doc/validation.txt')])
293 return suite
294
295 if __name__ == '__main__':
296 print('to test use test.py %s' % __file__)
297