blob: f3fa1b8c5815c65985c377a1d366e9957d75ed24 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001# test for xml.dom.minidom
Paul Prescod7993bcc2000-07-01 14:54:16 +00002
Martin v. Löwisfd6aaa12003-01-25 22:02:52 +00003import pickle
Florent Xiclunaf15351d2010-03-13 23:24:31 +00004from test.support import verbose, run_unittest, findfile
Guido van Rossumd8faa362007-04-27 19:54:29 +00005import unittest
Fred Drake17647f52000-07-03 16:37:42 +00006
Thomas Wouters0e3f5912006-08-11 14:57:12 +00007import xml.dom
8import xml.dom.minidom
9import xml.parsers.expat
Fred Drakec441f7b2002-07-19 22:16:41 +000010
Thomas Wouters0e3f5912006-08-11 14:57:12 +000011from xml.dom.minidom import parse, Node, Document, parseString
12from xml.dom.minidom import getDOMImplementation
Martin v. Löwisfd6aaa12003-01-25 22:02:52 +000013
Fred Drakec441f7b2002-07-19 22:16:41 +000014
Florent Xiclunaf15351d2010-03-13 23:24:31 +000015tstfile = findfile("test.xml", subdir="xmltestdata")
16
Paul Prescod7993bcc2000-07-01 14:54:16 +000017
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000018# The tests of DocumentType importing use these helpers to construct
19# the documents to work with, since not all DOM builders actually
20# create the DocumentType nodes.
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000021def create_doc_without_doctype(doctype=None):
22 return getDOMImplementation().createDocument(None, "doc", doctype)
23
24def create_nonempty_doctype():
25 doctype = getDOMImplementation().createDocumentType("doc", None, None)
26 doctype.entities._seq = []
27 doctype.notations._seq = []
Thomas Wouters0e3f5912006-08-11 14:57:12 +000028 notation = xml.dom.minidom.Notation("my-notation", None,
29 "http://xml.python.org/notations/my")
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000030 doctype.notations._seq.append(notation)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000031 entity = xml.dom.minidom.Entity("my-entity", None,
32 "http://xml.python.org/entities/my",
33 "my-notation")
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000034 entity.version = "1.0"
35 entity.encoding = "utf-8"
36 entity.actualEncoding = "us-ascii"
37 doctype.entities._seq.append(entity)
38 return doctype
39
40def create_doc_with_doctype():
41 doctype = create_nonempty_doctype()
42 doc = create_doc_without_doctype(doctype)
43 doctype.entities.item(0).ownerDocument = doc
44 doctype.notations.item(0).ownerDocument = doc
45 return doc
46
Guido van Rossumd8faa362007-04-27 19:54:29 +000047class MinidomTest(unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +000048 def confirm(self, test, testname = "Test"):
49 self.assertTrue(test, testname)
50
51 def checkWholeText(self, node, s):
52 t = node.wholeText
53 self.confirm(t == s, "looking for %s, found %s" % (repr(s), repr(t)))
54
55 def testParseFromFile(self):
Brett Cannonc5246922010-10-30 00:14:59 +000056 with open(tstfile) as file:
57 dom = parse(file)
58 dom.unlink()
59 self.confirm(isinstance(dom, Document))
Guido van Rossumd8faa362007-04-27 19:54:29 +000060
61 def testGetElementsByTagName(self):
62 dom = parse(tstfile)
63 self.confirm(dom.getElementsByTagName("LI") == \
64 dom.documentElement.getElementsByTagName("LI"))
65 dom.unlink()
66
67 def testInsertBefore(self):
68 dom = parseString("<doc><foo/></doc>")
69 root = dom.documentElement
70 elem = root.childNodes[0]
71 nelem = dom.createElement("element")
72 root.insertBefore(nelem, elem)
73 self.confirm(len(root.childNodes) == 2
74 and root.childNodes.length == 2
75 and root.childNodes[0] is nelem
76 and root.childNodes.item(0) is nelem
77 and root.childNodes[1] is elem
78 and root.childNodes.item(1) is elem
79 and root.firstChild is nelem
80 and root.lastChild is elem
81 and root.toxml() == "<doc><element/><foo/></doc>"
82 , "testInsertBefore -- node properly placed in tree")
83 nelem = dom.createElement("element")
84 root.insertBefore(nelem, None)
85 self.confirm(len(root.childNodes) == 3
86 and root.childNodes.length == 3
87 and root.childNodes[1] is elem
88 and root.childNodes.item(1) is elem
89 and root.childNodes[2] is nelem
90 and root.childNodes.item(2) is nelem
91 and root.lastChild is nelem
92 and nelem.previousSibling is elem
93 and root.toxml() == "<doc><element/><foo/><element/></doc>"
94 , "testInsertBefore -- node properly placed in tree")
95 nelem2 = dom.createElement("bar")
96 root.insertBefore(nelem2, nelem)
97 self.confirm(len(root.childNodes) == 4
98 and root.childNodes.length == 4
99 and root.childNodes[2] is nelem2
100 and root.childNodes.item(2) is nelem2
101 and root.childNodes[3] is nelem
102 and root.childNodes.item(3) is nelem
103 and nelem2.nextSibling is nelem
104 and nelem.previousSibling is nelem2
105 and root.toxml() ==
106 "<doc><element/><foo/><bar/><element/></doc>"
107 , "testInsertBefore -- node properly placed in tree")
108 dom.unlink()
109
110 def _create_fragment_test_nodes(self):
111 dom = parseString("<doc/>")
112 orig = dom.createTextNode("original")
113 c1 = dom.createTextNode("foo")
114 c2 = dom.createTextNode("bar")
115 c3 = dom.createTextNode("bat")
116 dom.documentElement.appendChild(orig)
117 frag = dom.createDocumentFragment()
118 frag.appendChild(c1)
119 frag.appendChild(c2)
120 frag.appendChild(c3)
121 return dom, orig, c1, c2, c3, frag
122
123 def testInsertBeforeFragment(self):
124 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
125 dom.documentElement.insertBefore(frag, None)
126 self.confirm(tuple(dom.documentElement.childNodes) ==
127 (orig, c1, c2, c3),
128 "insertBefore(<fragment>, None)")
129 frag.unlink()
130 dom.unlink()
131
132 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
133 dom.documentElement.insertBefore(frag, orig)
134 self.confirm(tuple(dom.documentElement.childNodes) ==
135 (c1, c2, c3, orig),
136 "insertBefore(<fragment>, orig)")
137 frag.unlink()
138 dom.unlink()
139
140 def testAppendChild(self):
141 dom = parse(tstfile)
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000142 dom.documentElement.appendChild(dom.createComment("Hello"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000143 self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment")
144 self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
145 dom.unlink()
146
147 def testAppendChildFragment(self):
148 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
149 dom.documentElement.appendChild(frag)
150 self.confirm(tuple(dom.documentElement.childNodes) ==
151 (orig, c1, c2, c3),
152 "appendChild(<fragment>)")
153 frag.unlink()
154 dom.unlink()
155
156 def testReplaceChildFragment(self):
157 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
158 dom.documentElement.replaceChild(frag, orig)
159 orig.unlink()
160 self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3),
161 "replaceChild(<fragment>)")
162 frag.unlink()
163 dom.unlink()
164
165 def testLegalChildren(self):
166 dom = Document()
167 elem = dom.createElement('element')
168 text = dom.createTextNode('text')
169 self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text)
170
171 dom.appendChild(elem)
172 self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text,
173 elem)
174 self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text,
175 elem)
176
177 nodemap = elem.attributes
178 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem,
179 text)
180 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS,
181 text)
182
183 elem.appendChild(text)
184 dom.unlink()
185
186 def testNamedNodeMapSetItem(self):
187 dom = Document()
188 elem = dom.createElement('element')
189 attrs = elem.attributes
190 attrs["foo"] = "bar"
191 a = attrs.item(0)
192 self.confirm(a.ownerDocument is dom,
193 "NamedNodeMap.__setitem__() sets ownerDocument")
194 self.confirm(a.ownerElement is elem,
195 "NamedNodeMap.__setitem__() sets ownerElement")
196 self.confirm(a.value == "bar",
197 "NamedNodeMap.__setitem__() sets value")
198 self.confirm(a.nodeValue == "bar",
199 "NamedNodeMap.__setitem__() sets nodeValue")
200 elem.unlink()
201 dom.unlink()
202
203 def testNonZero(self):
204 dom = parse(tstfile)
205 self.confirm(dom)# should not be zero
206 dom.appendChild(dom.createComment("foo"))
207 self.confirm(not dom.childNodes[-1].childNodes)
208 dom.unlink()
209
210 def testUnlink(self):
211 dom = parse(tstfile)
Kristján Valur Jónsson17173cf2010-06-09 08:13:42 +0000212 self.assertTrue(dom.childNodes)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000213 dom.unlink()
Kristján Valur Jónsson17173cf2010-06-09 08:13:42 +0000214 self.assertFalse(dom.childNodes)
215
216 def testContext(self):
217 with parse(tstfile) as dom:
218 self.assertTrue(dom.childNodes)
219 self.assertFalse(dom.childNodes)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000220
221 def testElement(self):
222 dom = Document()
223 dom.appendChild(dom.createElement("abc"))
224 self.confirm(dom.documentElement)
225 dom.unlink()
226
227 def testAAA(self):
228 dom = parseString("<abc/>")
229 el = dom.documentElement
230 el.setAttribute("spam", "jam2")
231 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA")
232 a = el.getAttributeNode("spam")
233 self.confirm(a.ownerDocument is dom,
234 "setAttribute() sets ownerDocument")
235 self.confirm(a.ownerElement is dom.documentElement,
236 "setAttribute() sets ownerElement")
237 dom.unlink()
238
239 def testAAB(self):
240 dom = parseString("<abc/>")
241 el = dom.documentElement
242 el.setAttribute("spam", "jam")
243 el.setAttribute("spam", "jam2")
244 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB")
245 dom.unlink()
246
247 def testAddAttr(self):
248 dom = Document()
249 child = dom.appendChild(dom.createElement("abc"))
250
251 child.setAttribute("def", "ghi")
252 self.confirm(child.getAttribute("def") == "ghi")
253 self.confirm(child.attributes["def"].value == "ghi")
254
255 child.setAttribute("jkl", "mno")
256 self.confirm(child.getAttribute("jkl") == "mno")
257 self.confirm(child.attributes["jkl"].value == "mno")
258
259 self.confirm(len(child.attributes) == 2)
260
261 child.setAttribute("def", "newval")
262 self.confirm(child.getAttribute("def") == "newval")
263 self.confirm(child.attributes["def"].value == "newval")
264
265 self.confirm(len(child.attributes) == 2)
266 dom.unlink()
267
268 def testDeleteAttr(self):
269 dom = Document()
270 child = dom.appendChild(dom.createElement("abc"))
271
272 self.confirm(len(child.attributes) == 0)
273 child.setAttribute("def", "ghi")
274 self.confirm(len(child.attributes) == 1)
275 del child.attributes["def"]
276 self.confirm(len(child.attributes) == 0)
277 dom.unlink()
278
279 def testRemoveAttr(self):
280 dom = Document()
281 child = dom.appendChild(dom.createElement("abc"))
282
283 child.setAttribute("def", "ghi")
284 self.confirm(len(child.attributes) == 1)
285 child.removeAttribute("def")
286 self.confirm(len(child.attributes) == 0)
287 dom.unlink()
288
289 def testRemoveAttrNS(self):
290 dom = Document()
291 child = dom.appendChild(
292 dom.createElementNS("http://www.python.org", "python:abc"))
293 child.setAttributeNS("http://www.w3.org", "xmlns:python",
294 "http://www.python.org")
295 child.setAttributeNS("http://www.python.org", "python:abcattr", "foo")
296 self.confirm(len(child.attributes) == 2)
297 child.removeAttributeNS("http://www.python.org", "abcattr")
298 self.confirm(len(child.attributes) == 1)
299 dom.unlink()
300
301 def testRemoveAttributeNode(self):
302 dom = Document()
303 child = dom.appendChild(dom.createElement("foo"))
304 child.setAttribute("spam", "jam")
305 self.confirm(len(child.attributes) == 1)
306 node = child.getAttributeNode("spam")
307 child.removeAttributeNode(node)
308 self.confirm(len(child.attributes) == 0
309 and child.getAttributeNode("spam") is None)
310 dom.unlink()
311
312 def testChangeAttr(self):
313 dom = parseString("<abc/>")
314 el = dom.documentElement
315 el.setAttribute("spam", "jam")
316 self.confirm(len(el.attributes) == 1)
317 el.setAttribute("spam", "bam")
318 # Set this attribute to be an ID and make sure that doesn't change
319 # when changing the value:
320 el.setIdAttribute("spam")
321 self.confirm(len(el.attributes) == 1
322 and el.attributes["spam"].value == "bam"
323 and el.attributes["spam"].nodeValue == "bam"
324 and el.getAttribute("spam") == "bam"
325 and el.getAttributeNode("spam").isId)
326 el.attributes["spam"] = "ham"
327 self.confirm(len(el.attributes) == 1
328 and el.attributes["spam"].value == "ham"
329 and el.attributes["spam"].nodeValue == "ham"
330 and el.getAttribute("spam") == "ham"
331 and el.attributes["spam"].isId)
332 el.setAttribute("spam2", "bam")
333 self.confirm(len(el.attributes) == 2
334 and el.attributes["spam"].value == "ham"
335 and el.attributes["spam"].nodeValue == "ham"
336 and el.getAttribute("spam") == "ham"
337 and el.attributes["spam2"].value == "bam"
338 and el.attributes["spam2"].nodeValue == "bam"
339 and el.getAttribute("spam2") == "bam")
340 el.attributes["spam2"] = "bam2"
341 self.confirm(len(el.attributes) == 2
342 and el.attributes["spam"].value == "ham"
343 and el.attributes["spam"].nodeValue == "ham"
344 and el.getAttribute("spam") == "ham"
345 and el.attributes["spam2"].value == "bam2"
346 and el.attributes["spam2"].nodeValue == "bam2"
347 and el.getAttribute("spam2") == "bam2")
348 dom.unlink()
349
350 def testGetAttrList(self):
351 pass
352
353 def testGetAttrValues(self): pass
354
355 def testGetAttrLength(self): pass
356
357 def testGetAttribute(self): pass
358
359 def testGetAttributeNS(self): pass
360
361 def testGetAttributeNode(self): pass
362
363 def testGetElementsByTagNameNS(self):
364 d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'>
365 <minidom:myelem/>
366 </foo>"""
367 dom = parseString(d)
368 elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom",
369 "myelem")
370 self.confirm(len(elems) == 1
371 and elems[0].namespaceURI == "http://pyxml.sf.net/minidom"
372 and elems[0].localName == "myelem"
373 and elems[0].prefix == "minidom"
374 and elems[0].tagName == "minidom:myelem"
375 and elems[0].nodeName == "minidom:myelem")
376 dom.unlink()
377
378 def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri,
379 lname):
380 nodelist = doc.getElementsByTagNameNS(nsuri, lname)
381 self.confirm(len(nodelist) == 0)
382
383 def testGetEmptyNodeListFromElementsByTagNameNS(self):
384 doc = parseString('<doc/>')
385 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
386 doc, 'http://xml.python.org/namespaces/a', 'localname')
387 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
388 doc, '*', 'splat')
389 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
390 doc, 'http://xml.python.org/namespaces/a', '*')
391
392 doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>')
393 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
394 doc, "http://xml.python.org/splat", "not-there")
395 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
396 doc, "*", "not-there")
397 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
398 doc, "http://somewhere.else.net/not-there", "e")
399
400 def testElementReprAndStr(self):
401 dom = Document()
402 el = dom.appendChild(dom.createElement("abc"))
403 string1 = repr(el)
404 string2 = str(el)
405 self.confirm(string1 == string2)
406 dom.unlink()
407
408 def testElementReprAndStrUnicode(self):
409 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000410 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000411 string1 = repr(el)
412 string2 = str(el)
413 self.confirm(string1 == string2)
414 dom.unlink()
415
416 def testElementReprAndStrUnicodeNS(self):
417 dom = Document()
418 el = dom.appendChild(
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000419 dom.createElementNS("http://www.slashdot.org", "slash:abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000420 string1 = repr(el)
421 string2 = str(el)
422 self.confirm(string1 == string2)
Ezio Melotti7fb4da72010-03-18 12:29:13 +0000423 self.confirm("slash:abc" in string1)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000424 dom.unlink()
425
426 def testAttributeRepr(self):
427 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000428 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000429 node = el.setAttribute("abc", "def")
430 self.confirm(str(node) == repr(node))
431 dom.unlink()
432
433 def testTextNodeRepr(self): pass
434
435 def testWriteXML(self):
436 str = '<?xml version="1.0" ?><a b="c"/>'
437 dom = parseString(str)
438 domstr = dom.toxml()
439 dom.unlink()
440 self.confirm(str == domstr)
441
442 def testAltNewline(self):
443 str = '<?xml version="1.0" ?>\n<a b="c"/>\n'
444 dom = parseString(str)
445 domstr = dom.toprettyxml(newl="\r\n")
446 dom.unlink()
447 self.confirm(domstr == str.replace("\n", "\r\n"))
448
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200449 def test_toprettyxml_with_text_nodes(self):
450 # see issue #4147, text nodes are not indented
451 decl = '<?xml version="1.0" ?>\n'
452 self.assertEqual(parseString('<B>A</B>').toprettyxml(),
453 decl + '<B>A</B>\n')
454 self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(),
455 decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n')
456 self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(),
457 decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n')
458 self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(),
459 decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')
460 self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(),
461 decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n')
462
463 def test_toprettyxml_with_adjacent_text_nodes(self):
464 # see issue #4147, adjacent text nodes are indented normally
465 dom = Document()
466 elem = dom.createElement('elem')
467 elem.appendChild(dom.createTextNode('TEXT'))
468 elem.appendChild(dom.createTextNode('TEXT'))
469 dom.appendChild(elem)
470 decl = '<?xml version="1.0" ?>\n'
471 self.assertEqual(dom.toprettyxml(),
472 decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n')
473
Éric Araujo63ba97b2011-10-05 01:29:22 +0200474 def test_toprettyxml_preserves_content_of_text_node(self):
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200475 # see issue #4147
476 for str in ('<B>A</B>', '<A><B>C</B></A>'):
477 dom = parseString(str)
478 dom2 = parseString(dom.toprettyxml())
479 self.assertEqual(
480 dom.getElementsByTagName('B')[0].childNodes[0].toxml(),
481 dom2.getElementsByTagName('B')[0].childNodes[0].toxml())
R David Murray791744b2011-10-01 16:19:51 -0400482
Guido van Rossumd8faa362007-04-27 19:54:29 +0000483 def testProcessingInstruction(self):
484 dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
485 pi = dom.documentElement.firstChild
486 self.confirm(pi.target == "mypi"
487 and pi.data == "data \t\n "
488 and pi.nodeName == "mypi"
489 and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE
490 and pi.attributes is None
491 and not pi.hasChildNodes()
492 and len(pi.childNodes) == 0
493 and pi.firstChild is None
494 and pi.lastChild is None
495 and pi.localName is None
496 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
497
498 def testProcessingInstructionRepr(self): pass
499
500 def testTextRepr(self): pass
501
502 def testWriteText(self): pass
503
504 def testDocumentElement(self): pass
505
506 def testTooManyDocumentElements(self):
507 doc = parseString("<doc/>")
508 elem = doc.createElement("extra")
509 # Should raise an exception when adding an extra document element.
510 self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem)
511 elem.unlink()
512 doc.unlink()
513
514 def testCreateElementNS(self): pass
515
516 def testCreateAttributeNS(self): pass
517
518 def testParse(self): pass
519
520 def testParseString(self): pass
521
522 def testComment(self): pass
523
524 def testAttrListItem(self): pass
525
526 def testAttrListItems(self): pass
527
528 def testAttrListItemNS(self): pass
529
530 def testAttrListKeys(self): pass
531
532 def testAttrListKeysNS(self): pass
533
534 def testRemoveNamedItem(self):
535 doc = parseString("<doc a=''/>")
536 e = doc.documentElement
537 attrs = e.attributes
538 a1 = e.getAttributeNode("a")
539 a2 = attrs.removeNamedItem("a")
540 self.confirm(a1.isSameNode(a2))
541 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a")
542
543 def testRemoveNamedItemNS(self):
544 doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>")
545 e = doc.documentElement
546 attrs = e.attributes
547 a1 = e.getAttributeNodeNS("http://xml.python.org/", "b")
548 a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b")
549 self.confirm(a1.isSameNode(a2))
550 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
551 "http://xml.python.org/", "b")
552
553 def testAttrListValues(self): pass
554
555 def testAttrListLength(self): pass
556
557 def testAttrList__getitem__(self): pass
558
559 def testAttrList__setitem__(self): pass
560
561 def testSetAttrValueandNodeValue(self): pass
562
563 def testParseElement(self): pass
564
565 def testParseAttributes(self): pass
566
567 def testParseElementNamespaces(self): pass
568
569 def testParseAttributeNamespaces(self): pass
570
571 def testParseProcessingInstructions(self): pass
572
573 def testChildNodes(self): pass
574
575 def testFirstChild(self): pass
576
577 def testHasChildNodes(self): pass
578
579 def _testCloneElementCopiesAttributes(self, e1, e2, test):
580 attrs1 = e1.attributes
581 attrs2 = e2.attributes
582 keys1 = list(attrs1.keys())
583 keys2 = list(attrs2.keys())
584 keys1.sort()
585 keys2.sort()
586 self.confirm(keys1 == keys2, "clone of element has same attribute keys")
587 for i in range(len(keys1)):
588 a1 = attrs1.item(i)
589 a2 = attrs2.item(i)
590 self.confirm(a1 is not a2
591 and a1.value == a2.value
592 and a1.nodeValue == a2.nodeValue
593 and a1.namespaceURI == a2.namespaceURI
594 and a1.localName == a2.localName
595 , "clone of attribute node has proper attribute values")
596 self.confirm(a2.ownerElement is e2,
597 "clone of attribute node correctly owned")
598
599 def _setupCloneElement(self, deep):
600 dom = parseString("<doc attr='value'><foo/></doc>")
601 root = dom.documentElement
602 clone = root.cloneNode(deep)
603 self._testCloneElementCopiesAttributes(
604 root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
605 # mutilate the original so shared data is detected
606 root.tagName = root.nodeName = "MODIFIED"
607 root.setAttribute("attr", "NEW VALUE")
608 root.setAttribute("added", "VALUE")
609 return dom, clone
610
611 def testCloneElementShallow(self):
612 dom, clone = self._setupCloneElement(0)
613 self.confirm(len(clone.childNodes) == 0
614 and clone.childNodes.length == 0
615 and clone.parentNode is None
616 and clone.toxml() == '<doc attr="value"/>'
617 , "testCloneElementShallow")
618 dom.unlink()
619
620 def testCloneElementDeep(self):
621 dom, clone = self._setupCloneElement(1)
622 self.confirm(len(clone.childNodes) == 1
623 and clone.childNodes.length == 1
624 and clone.parentNode is None
625 and clone.toxml() == '<doc attr="value"><foo/></doc>'
626 , "testCloneElementDeep")
627 dom.unlink()
628
629 def testCloneDocumentShallow(self):
630 doc = parseString("<?xml version='1.0'?>\n"
631 "<!-- comment -->"
632 "<!DOCTYPE doc [\n"
633 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
634 "]>\n"
635 "<doc attr='value'/>")
636 doc2 = doc.cloneNode(0)
637 self.confirm(doc2 is None,
638 "testCloneDocumentShallow:"
639 " shallow cloning of documents makes no sense!")
640
641 def testCloneDocumentDeep(self):
642 doc = parseString("<?xml version='1.0'?>\n"
643 "<!-- comment -->"
644 "<!DOCTYPE doc [\n"
645 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
646 "]>\n"
647 "<doc attr='value'/>")
648 doc2 = doc.cloneNode(1)
649 self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)),
650 "testCloneDocumentDeep: document objects not distinct")
651 self.confirm(len(doc.childNodes) == len(doc2.childNodes),
652 "testCloneDocumentDeep: wrong number of Document children")
653 self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE,
654 "testCloneDocumentDeep: documentElement not an ELEMENT_NODE")
655 self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2),
656 "testCloneDocumentDeep: documentElement owner is not new document")
657 self.confirm(not doc.documentElement.isSameNode(doc2.documentElement),
658 "testCloneDocumentDeep: documentElement should not be shared")
659 if doc.doctype is not None:
660 # check the doctype iff the original DOM maintained it
661 self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE,
662 "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE")
663 self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2))
664 self.confirm(not doc.doctype.isSameNode(doc2.doctype))
665
666 def testCloneDocumentTypeDeepOk(self):
667 doctype = create_nonempty_doctype()
668 clone = doctype.cloneNode(1)
669 self.confirm(clone is not None
670 and clone.nodeName == doctype.nodeName
671 and clone.name == doctype.name
672 and clone.publicId == doctype.publicId
673 and clone.systemId == doctype.systemId
674 and len(clone.entities) == len(doctype.entities)
675 and clone.entities.item(len(clone.entities)) is None
676 and len(clone.notations) == len(doctype.notations)
677 and clone.notations.item(len(clone.notations)) is None
678 and len(clone.childNodes) == 0)
679 for i in range(len(doctype.entities)):
680 se = doctype.entities.item(i)
681 ce = clone.entities.item(i)
682 self.confirm((not se.isSameNode(ce))
683 and (not ce.isSameNode(se))
684 and ce.nodeName == se.nodeName
685 and ce.notationName == se.notationName
686 and ce.publicId == se.publicId
687 and ce.systemId == se.systemId
688 and ce.encoding == se.encoding
689 and ce.actualEncoding == se.actualEncoding
690 and ce.version == se.version)
691 for i in range(len(doctype.notations)):
692 sn = doctype.notations.item(i)
693 cn = clone.notations.item(i)
694 self.confirm((not sn.isSameNode(cn))
695 and (not cn.isSameNode(sn))
696 and cn.nodeName == sn.nodeName
697 and cn.publicId == sn.publicId
698 and cn.systemId == sn.systemId)
699
700 def testCloneDocumentTypeDeepNotOk(self):
701 doc = create_doc_with_doctype()
702 clone = doc.doctype.cloneNode(1)
703 self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk")
704
705 def testCloneDocumentTypeShallowOk(self):
706 doctype = create_nonempty_doctype()
707 clone = doctype.cloneNode(0)
708 self.confirm(clone is not None
709 and clone.nodeName == doctype.nodeName
710 and clone.name == doctype.name
711 and clone.publicId == doctype.publicId
712 and clone.systemId == doctype.systemId
713 and len(clone.entities) == 0
714 and clone.entities.item(0) is None
715 and len(clone.notations) == 0
716 and clone.notations.item(0) is None
717 and len(clone.childNodes) == 0)
718
719 def testCloneDocumentTypeShallowNotOk(self):
720 doc = create_doc_with_doctype()
721 clone = doc.doctype.cloneNode(0)
722 self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk")
723
724 def check_import_document(self, deep, testName):
725 doc1 = parseString("<doc/>")
726 doc2 = parseString("<doc/>")
727 self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep)
728
729 def testImportDocumentShallow(self):
730 self.check_import_document(0, "testImportDocumentShallow")
731
732 def testImportDocumentDeep(self):
733 self.check_import_document(1, "testImportDocumentDeep")
734
735 def testImportDocumentTypeShallow(self):
736 src = create_doc_with_doctype()
737 target = create_doc_without_doctype()
738 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
739 src.doctype, 0)
740
741 def testImportDocumentTypeDeep(self):
742 src = create_doc_with_doctype()
743 target = create_doc_without_doctype()
744 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
745 src.doctype, 1)
746
747 # Testing attribute clones uses a helper, and should always be deep,
748 # even if the argument to cloneNode is false.
749 def check_clone_attribute(self, deep, testName):
750 doc = parseString("<doc attr='value'/>")
751 attr = doc.documentElement.getAttributeNode("attr")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000752 self.assertNotEqual(attr, None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000753 clone = attr.cloneNode(deep)
754 self.confirm(not clone.isSameNode(attr))
755 self.confirm(not attr.isSameNode(clone))
756 self.confirm(clone.ownerElement is None,
757 testName + ": ownerElement should be None")
758 self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument),
759 testName + ": ownerDocument does not match")
760 self.confirm(clone.specified,
761 testName + ": cloned attribute must have specified == True")
762
763 def testCloneAttributeShallow(self):
764 self.check_clone_attribute(0, "testCloneAttributeShallow")
765
766 def testCloneAttributeDeep(self):
767 self.check_clone_attribute(1, "testCloneAttributeDeep")
768
769 def check_clone_pi(self, deep, testName):
770 doc = parseString("<?target data?><doc/>")
771 pi = doc.firstChild
Ezio Melottib3aedd42010-11-20 19:04:17 +0000772 self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000773 clone = pi.cloneNode(deep)
774 self.confirm(clone.target == pi.target
775 and clone.data == pi.data)
776
777 def testClonePIShallow(self):
778 self.check_clone_pi(0, "testClonePIShallow")
779
780 def testClonePIDeep(self):
781 self.check_clone_pi(1, "testClonePIDeep")
782
783 def testNormalize(self):
784 doc = parseString("<doc/>")
785 root = doc.documentElement
786 root.appendChild(doc.createTextNode("first"))
787 root.appendChild(doc.createTextNode("second"))
788 self.confirm(len(root.childNodes) == 2
789 and root.childNodes.length == 2,
790 "testNormalize -- preparation")
791 doc.normalize()
792 self.confirm(len(root.childNodes) == 1
793 and root.childNodes.length == 1
794 and root.firstChild is root.lastChild
795 and root.firstChild.data == "firstsecond"
796 , "testNormalize -- result")
797 doc.unlink()
798
799 doc = parseString("<doc/>")
800 root = doc.documentElement
801 root.appendChild(doc.createTextNode(""))
802 doc.normalize()
803 self.confirm(len(root.childNodes) == 0
804 and root.childNodes.length == 0,
805 "testNormalize -- single empty node removed")
806 doc.unlink()
807
R. David Murraydc6da8a2009-04-09 22:16:43 +0000808 def testNormalizeCombineAndNextSibling(self):
809 doc = parseString("<doc/>")
810 root = doc.documentElement
811 root.appendChild(doc.createTextNode("first"))
812 root.appendChild(doc.createTextNode("second"))
813 root.appendChild(doc.createElement("i"))
814 self.confirm(len(root.childNodes) == 3
815 and root.childNodes.length == 3,
816 "testNormalizeCombineAndNextSibling -- preparation")
817 doc.normalize()
818 self.confirm(len(root.childNodes) == 2
819 and root.childNodes.length == 2
820 and root.firstChild.data == "firstsecond"
821 and root.firstChild is not root.lastChild
822 and root.firstChild.nextSibling is root.lastChild
823 and root.firstChild.previousSibling is None
824 and root.lastChild.previousSibling is root.firstChild
825 and root.lastChild.nextSibling is None
826 , "testNormalizeCombinedAndNextSibling -- result")
827 doc.unlink()
828
829 def testNormalizeDeleteWithPrevSibling(self):
830 doc = parseString("<doc/>")
831 root = doc.documentElement
832 root.appendChild(doc.createTextNode("first"))
833 root.appendChild(doc.createTextNode(""))
834 self.confirm(len(root.childNodes) == 2
835 and root.childNodes.length == 2,
836 "testNormalizeDeleteWithPrevSibling -- preparation")
837 doc.normalize()
838 self.confirm(len(root.childNodes) == 1
839 and root.childNodes.length == 1
840 and root.firstChild.data == "first"
841 and root.firstChild is root.lastChild
842 and root.firstChild.nextSibling is None
843 and root.firstChild.previousSibling is None
844 , "testNormalizeDeleteWithPrevSibling -- result")
845 doc.unlink()
846
847 def testNormalizeDeleteWithNextSibling(self):
848 doc = parseString("<doc/>")
849 root = doc.documentElement
850 root.appendChild(doc.createTextNode(""))
851 root.appendChild(doc.createTextNode("second"))
852 self.confirm(len(root.childNodes) == 2
853 and root.childNodes.length == 2,
854 "testNormalizeDeleteWithNextSibling -- preparation")
855 doc.normalize()
856 self.confirm(len(root.childNodes) == 1
857 and root.childNodes.length == 1
858 and root.firstChild.data == "second"
859 and root.firstChild is root.lastChild
860 and root.firstChild.nextSibling is None
861 and root.firstChild.previousSibling is None
862 , "testNormalizeDeleteWithNextSibling -- result")
863 doc.unlink()
864
865 def testNormalizeDeleteWithTwoNonTextSiblings(self):
866 doc = parseString("<doc/>")
867 root = doc.documentElement
868 root.appendChild(doc.createElement("i"))
869 root.appendChild(doc.createTextNode(""))
870 root.appendChild(doc.createElement("i"))
871 self.confirm(len(root.childNodes) == 3
872 and root.childNodes.length == 3,
873 "testNormalizeDeleteWithTwoSiblings -- preparation")
874 doc.normalize()
875 self.confirm(len(root.childNodes) == 2
876 and root.childNodes.length == 2
877 and root.firstChild is not root.lastChild
878 and root.firstChild.nextSibling is root.lastChild
879 and root.firstChild.previousSibling is None
880 and root.lastChild.previousSibling is root.firstChild
881 and root.lastChild.nextSibling is None
882 , "testNormalizeDeleteWithTwoSiblings -- result")
883 doc.unlink()
884
885 def testNormalizeDeleteAndCombine(self):
886 doc = parseString("<doc/>")
887 root = doc.documentElement
888 root.appendChild(doc.createTextNode(""))
889 root.appendChild(doc.createTextNode("second"))
890 root.appendChild(doc.createTextNode(""))
891 root.appendChild(doc.createTextNode("fourth"))
892 root.appendChild(doc.createTextNode(""))
893 self.confirm(len(root.childNodes) == 5
894 and root.childNodes.length == 5,
895 "testNormalizeDeleteAndCombine -- preparation")
896 doc.normalize()
897 self.confirm(len(root.childNodes) == 1
898 and root.childNodes.length == 1
899 and root.firstChild is root.lastChild
900 and root.firstChild.data == "secondfourth"
901 and root.firstChild.previousSibling is None
902 and root.firstChild.nextSibling is None
903 , "testNormalizeDeleteAndCombine -- result")
904 doc.unlink()
905
906 def testNormalizeRecursion(self):
907 doc = parseString("<doc>"
908 "<o>"
909 "<i/>"
910 "t"
911 #
912 #x
913 "</o>"
914 "<o>"
915 "<o>"
916 "t2"
917 #x2
918 "</o>"
919 "t3"
920 #x3
921 "</o>"
922 #
923 "</doc>")
924 root = doc.documentElement
925 root.childNodes[0].appendChild(doc.createTextNode(""))
926 root.childNodes[0].appendChild(doc.createTextNode("x"))
927 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
928 root.childNodes[1].appendChild(doc.createTextNode("x3"))
929 root.appendChild(doc.createTextNode(""))
930 self.confirm(len(root.childNodes) == 3
931 and root.childNodes.length == 3
932 and len(root.childNodes[0].childNodes) == 4
933 and root.childNodes[0].childNodes.length == 4
934 and len(root.childNodes[1].childNodes) == 3
935 and root.childNodes[1].childNodes.length == 3
936 and len(root.childNodes[1].childNodes[0].childNodes) == 2
937 and root.childNodes[1].childNodes[0].childNodes.length == 2
938 , "testNormalize2 -- preparation")
939 doc.normalize()
940 self.confirm(len(root.childNodes) == 2
941 and root.childNodes.length == 2
942 and len(root.childNodes[0].childNodes) == 2
943 and root.childNodes[0].childNodes.length == 2
944 and len(root.childNodes[1].childNodes) == 2
945 and root.childNodes[1].childNodes.length == 2
946 and len(root.childNodes[1].childNodes[0].childNodes) == 1
947 and root.childNodes[1].childNodes[0].childNodes.length == 1
948 , "testNormalize2 -- childNodes lengths")
949 self.confirm(root.childNodes[0].childNodes[1].data == "tx"
950 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
951 and root.childNodes[1].childNodes[1].data == "t3x3"
952 , "testNormalize2 -- joined text fields")
953 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
954 and root.childNodes[0].childNodes[1].previousSibling
955 is root.childNodes[0].childNodes[0]
956 and root.childNodes[0].childNodes[0].previousSibling is None
957 and root.childNodes[0].childNodes[0].nextSibling
958 is root.childNodes[0].childNodes[1]
959 and root.childNodes[1].childNodes[1].nextSibling is None
960 and root.childNodes[1].childNodes[1].previousSibling
961 is root.childNodes[1].childNodes[0]
962 and root.childNodes[1].childNodes[0].previousSibling is None
963 and root.childNodes[1].childNodes[0].nextSibling
964 is root.childNodes[1].childNodes[1]
965 , "testNormalize2 -- sibling pointers")
966 doc.unlink()
967
968
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +0000969 def testBug0777884(self):
970 doc = parseString("<o>text</o>")
971 text = doc.documentElement.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000972 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +0000973 # Should run quietly, doing nothing.
974 text.normalize()
975 doc.unlink()
976
Christian Heimes05e8be12008-02-23 18:30:17 +0000977 def testBug1433694(self):
978 doc = parseString("<o><i/>t</o>")
979 node = doc.documentElement
980 node.childNodes[1].nodeValue = ""
981 node.normalize()
Florent Xicluna9b86b9a2010-03-19 19:00:44 +0000982 self.confirm(node.childNodes[-1].nextSibling is None,
Christian Heimes05e8be12008-02-23 18:30:17 +0000983 "Final child's .nextSibling should be None")
984
Guido van Rossumd8faa362007-04-27 19:54:29 +0000985 def testSiblings(self):
986 doc = parseString("<doc><?pi?>text?<elm/></doc>")
987 root = doc.documentElement
988 (pi, text, elm) = root.childNodes
989
990 self.confirm(pi.nextSibling is text and
991 pi.previousSibling is None and
992 text.nextSibling is elm and
993 text.previousSibling is pi and
994 elm.nextSibling is None and
995 elm.previousSibling is text, "testSiblings")
996
997 doc.unlink()
998
999 def testParents(self):
1000 doc = parseString(
1001 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
1002 root = doc.documentElement
1003 elm1 = root.childNodes[0]
1004 (elm2a, elm2b) = elm1.childNodes
1005 elm3 = elm2b.childNodes[0]
1006
1007 self.confirm(root.parentNode is doc and
1008 elm1.parentNode is root and
1009 elm2a.parentNode is elm1 and
1010 elm2b.parentNode is elm1 and
1011 elm3.parentNode is elm2b, "testParents")
1012 doc.unlink()
1013
1014 def testNodeListItem(self):
1015 doc = parseString("<doc><e/><e/></doc>")
1016 children = doc.childNodes
1017 docelem = children[0]
1018 self.confirm(children[0] is children.item(0)
1019 and children.item(1) is None
1020 and docelem.childNodes.item(0) is docelem.childNodes[0]
1021 and docelem.childNodes.item(1) is docelem.childNodes[1]
1022 and docelem.childNodes.item(0).childNodes.item(0) is None,
1023 "test NodeList.item()")
1024 doc.unlink()
1025
1026 def testSAX2DOM(self):
1027 from xml.dom import pulldom
1028
1029 sax2dom = pulldom.SAX2DOM()
1030 sax2dom.startDocument()
1031 sax2dom.startElement("doc", {})
1032 sax2dom.characters("text")
1033 sax2dom.startElement("subelm", {})
1034 sax2dom.characters("text")
1035 sax2dom.endElement("subelm")
1036 sax2dom.characters("text")
1037 sax2dom.endElement("doc")
1038 sax2dom.endDocument()
1039
1040 doc = sax2dom.document
1041 root = doc.documentElement
1042 (text1, elm1, text2) = root.childNodes
1043 text3 = elm1.childNodes[0]
1044
1045 self.confirm(text1.previousSibling is None and
1046 text1.nextSibling is elm1 and
1047 elm1.previousSibling is text1 and
1048 elm1.nextSibling is text2 and
1049 text2.previousSibling is elm1 and
1050 text2.nextSibling is None and
1051 text3.previousSibling is None and
1052 text3.nextSibling is None, "testSAX2DOM - siblings")
1053
1054 self.confirm(root.parentNode is doc and
1055 text1.parentNode is root and
1056 elm1.parentNode is root and
1057 text2.parentNode is root and
1058 text3.parentNode is elm1, "testSAX2DOM - parents")
1059 doc.unlink()
1060
1061 def testEncodings(self):
1062 doc = parseString('<foo>&#x20ac;</foo>')
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001063 self.assertEqual(doc.toxml(),
1064 '<?xml version="1.0" ?><foo>\u20ac</foo>')
1065 self.assertEqual(doc.toxml('utf-8'),
1066 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>')
1067 self.assertEqual(doc.toxml('iso-8859-15'),
1068 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001069
1070 # Verify that character decoding errors throw exceptions instead
1071 # of crashing
1072 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001073 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001074
1075 doc.unlink()
1076
1077 class UserDataHandler:
1078 called = 0
1079 def handle(self, operation, key, data, src, dst):
1080 dst.setUserData(key, data + 1, self)
1081 src.setUserData(key, None, None)
1082 self.called = 1
1083
1084 def testUserData(self):
1085 dom = Document()
1086 n = dom.createElement('e')
1087 self.confirm(n.getUserData("foo") is None)
1088 n.setUserData("foo", None, None)
1089 self.confirm(n.getUserData("foo") is None)
1090 n.setUserData("foo", 12, 12)
1091 n.setUserData("bar", 13, 13)
1092 self.confirm(n.getUserData("foo") == 12)
1093 self.confirm(n.getUserData("bar") == 13)
1094 n.setUserData("foo", None, None)
1095 self.confirm(n.getUserData("foo") is None)
1096 self.confirm(n.getUserData("bar") == 13)
1097
1098 handler = self.UserDataHandler()
1099 n.setUserData("bar", 12, handler)
1100 c = n.cloneNode(1)
1101 self.confirm(handler.called
1102 and n.getUserData("bar") is None
1103 and c.getUserData("bar") == 13)
1104 n.unlink()
1105 c.unlink()
1106 dom.unlink()
1107
1108 def checkRenameNodeSharedConstraints(self, doc, node):
1109 # Make sure illegal NS usage is detected:
1110 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1111 "http://xml.python.org/ns", "xmlns:foo")
1112 doc2 = parseString("<doc/>")
1113 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1114 xml.dom.EMPTY_NAMESPACE, "foo")
1115
1116 def testRenameAttribute(self):
1117 doc = parseString("<doc a='v'/>")
1118 elem = doc.documentElement
1119 attrmap = elem.attributes
1120 attr = elem.attributes['a']
1121
1122 # Simple renaming
1123 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1124 self.confirm(attr.name == "b"
1125 and attr.nodeName == "b"
1126 and attr.localName is None
1127 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1128 and attr.prefix is None
1129 and attr.value == "v"
1130 and elem.getAttributeNode("a") is None
1131 and elem.getAttributeNode("b").isSameNode(attr)
1132 and attrmap["b"].isSameNode(attr)
1133 and attr.ownerDocument.isSameNode(doc)
1134 and attr.ownerElement.isSameNode(elem))
1135
1136 # Rename to have a namespace, no prefix
1137 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1138 self.confirm(attr.name == "c"
1139 and attr.nodeName == "c"
1140 and attr.localName == "c"
1141 and attr.namespaceURI == "http://xml.python.org/ns"
1142 and attr.prefix is None
1143 and attr.value == "v"
1144 and elem.getAttributeNode("a") is None
1145 and elem.getAttributeNode("b") is None
1146 and elem.getAttributeNode("c").isSameNode(attr)
1147 and elem.getAttributeNodeNS(
1148 "http://xml.python.org/ns", "c").isSameNode(attr)
1149 and attrmap["c"].isSameNode(attr)
1150 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1151
1152 # Rename to have a namespace, with prefix
1153 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1154 self.confirm(attr.name == "p:d"
1155 and attr.nodeName == "p:d"
1156 and attr.localName == "d"
1157 and attr.namespaceURI == "http://xml.python.org/ns2"
1158 and attr.prefix == "p"
1159 and attr.value == "v"
1160 and elem.getAttributeNode("a") is None
1161 and elem.getAttributeNode("b") is None
1162 and elem.getAttributeNode("c") is None
1163 and elem.getAttributeNodeNS(
1164 "http://xml.python.org/ns", "c") is None
1165 and elem.getAttributeNode("p:d").isSameNode(attr)
1166 and elem.getAttributeNodeNS(
1167 "http://xml.python.org/ns2", "d").isSameNode(attr)
1168 and attrmap["p:d"].isSameNode(attr)
1169 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1170
1171 # Rename back to a simple non-NS node
1172 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1173 self.confirm(attr.name == "e"
1174 and attr.nodeName == "e"
1175 and attr.localName is None
1176 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1177 and attr.prefix is None
1178 and attr.value == "v"
1179 and elem.getAttributeNode("a") is None
1180 and elem.getAttributeNode("b") is None
1181 and elem.getAttributeNode("c") is None
1182 and elem.getAttributeNode("p:d") is None
1183 and elem.getAttributeNodeNS(
1184 "http://xml.python.org/ns", "c") is None
1185 and elem.getAttributeNode("e").isSameNode(attr)
1186 and attrmap["e"].isSameNode(attr))
1187
1188 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1189 "http://xml.python.org/ns", "xmlns")
1190 self.checkRenameNodeSharedConstraints(doc, attr)
1191 doc.unlink()
1192
1193 def testRenameElement(self):
1194 doc = parseString("<doc/>")
1195 elem = doc.documentElement
1196
1197 # Simple renaming
1198 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1199 self.confirm(elem.tagName == "a"
1200 and elem.nodeName == "a"
1201 and elem.localName is None
1202 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1203 and elem.prefix is None
1204 and elem.ownerDocument.isSameNode(doc))
1205
1206 # Rename to have a namespace, no prefix
1207 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1208 self.confirm(elem.tagName == "b"
1209 and elem.nodeName == "b"
1210 and elem.localName == "b"
1211 and elem.namespaceURI == "http://xml.python.org/ns"
1212 and elem.prefix is None
1213 and elem.ownerDocument.isSameNode(doc))
1214
1215 # Rename to have a namespace, with prefix
1216 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1217 self.confirm(elem.tagName == "p:c"
1218 and elem.nodeName == "p:c"
1219 and elem.localName == "c"
1220 and elem.namespaceURI == "http://xml.python.org/ns2"
1221 and elem.prefix == "p"
1222 and elem.ownerDocument.isSameNode(doc))
1223
1224 # Rename back to a simple non-NS node
1225 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1226 self.confirm(elem.tagName == "d"
1227 and elem.nodeName == "d"
1228 and elem.localName is None
1229 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1230 and elem.prefix is None
1231 and elem.ownerDocument.isSameNode(doc))
1232
1233 self.checkRenameNodeSharedConstraints(doc, elem)
1234 doc.unlink()
1235
1236 def testRenameOther(self):
1237 # We have to create a comment node explicitly since not all DOM
1238 # builders used with minidom add comments to the DOM.
1239 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1240 xml.dom.EMPTY_NAMESPACE, "e", None)
1241 node = doc.createComment("comment")
1242 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1243 xml.dom.EMPTY_NAMESPACE, "foo")
1244 doc.unlink()
1245
1246 def testWholeText(self):
1247 doc = parseString("<doc>a</doc>")
1248 elem = doc.documentElement
1249 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001250 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001251
1252 self.checkWholeText(text, "a")
1253 elem.appendChild(doc.createTextNode("b"))
1254 self.checkWholeText(text, "ab")
1255 elem.insertBefore(doc.createCDATASection("c"), text)
1256 self.checkWholeText(text, "cab")
1257
1258 # make sure we don't cross other nodes
1259 splitter = doc.createComment("comment")
1260 elem.appendChild(splitter)
1261 text2 = doc.createTextNode("d")
1262 elem.appendChild(text2)
1263 self.checkWholeText(text, "cab")
1264 self.checkWholeText(text2, "d")
1265
1266 x = doc.createElement("x")
1267 elem.replaceChild(x, splitter)
1268 splitter = x
1269 self.checkWholeText(text, "cab")
1270 self.checkWholeText(text2, "d")
1271
1272 x = doc.createProcessingInstruction("y", "z")
1273 elem.replaceChild(x, splitter)
1274 splitter = x
1275 self.checkWholeText(text, "cab")
1276 self.checkWholeText(text2, "d")
1277
1278 elem.removeChild(splitter)
1279 self.checkWholeText(text, "cabd")
1280 self.checkWholeText(text2, "cabd")
1281
1282 def testPatch1094164(self):
1283 doc = parseString("<doc><e/></doc>")
1284 elem = doc.documentElement
1285 e = elem.firstChild
1286 self.confirm(e.parentNode is elem, "Before replaceChild()")
1287 # Check that replacing a child with itself leaves the tree unchanged
1288 elem.replaceChild(e, e)
1289 self.confirm(e.parentNode is elem, "After replaceChild()")
1290
1291 def testReplaceWholeText(self):
1292 def setup():
1293 doc = parseString("<doc>a<e/>d</doc>")
1294 elem = doc.documentElement
1295 text1 = elem.firstChild
1296 text2 = elem.lastChild
1297 splitter = text1.nextSibling
1298 elem.insertBefore(doc.createTextNode("b"), splitter)
1299 elem.insertBefore(doc.createCDATASection("c"), text1)
1300 return doc, elem, text1, splitter, text2
1301
1302 doc, elem, text1, splitter, text2 = setup()
1303 text = text1.replaceWholeText("new content")
1304 self.checkWholeText(text, "new content")
1305 self.checkWholeText(text2, "d")
1306 self.confirm(len(elem.childNodes) == 3)
1307
1308 doc, elem, text1, splitter, text2 = setup()
1309 text = text2.replaceWholeText("new content")
1310 self.checkWholeText(text, "new content")
1311 self.checkWholeText(text1, "cab")
1312 self.confirm(len(elem.childNodes) == 5)
1313
1314 doc, elem, text1, splitter, text2 = setup()
1315 text = text1.replaceWholeText("")
1316 self.checkWholeText(text2, "d")
1317 self.confirm(text is None
1318 and len(elem.childNodes) == 2)
1319
1320 def testSchemaType(self):
1321 doc = parseString(
1322 "<!DOCTYPE doc [\n"
1323 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1324 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1325 " <!ATTLIST doc id ID #IMPLIED \n"
1326 " ref IDREF #IMPLIED \n"
1327 " refs IDREFS #IMPLIED \n"
1328 " enum (a|b) #IMPLIED \n"
1329 " ent ENTITY #IMPLIED \n"
1330 " ents ENTITIES #IMPLIED \n"
1331 " nm NMTOKEN #IMPLIED \n"
1332 " nms NMTOKENS #IMPLIED \n"
1333 " text CDATA #IMPLIED \n"
1334 " >\n"
1335 "]><doc id='name' notid='name' text='splat!' enum='b'"
1336 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1337 " nm='123' nms='123 abc' />")
1338 elem = doc.documentElement
1339 # We don't want to rely on any specific loader at this point, so
1340 # just make sure we can get to all the names, and that the
1341 # DTD-based namespace is right. The names can vary by loader
1342 # since each supports a different level of DTD information.
1343 t = elem.schemaType
1344 self.confirm(t.name is None
1345 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1346 names = "id notid text enum ref refs ent ents nm nms".split()
1347 for name in names:
1348 a = elem.getAttributeNode(name)
1349 t = a.schemaType
1350 self.confirm(hasattr(t, "name")
1351 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1352
1353 def testSetIdAttribute(self):
1354 doc = parseString("<doc a1='v' a2='w'/>")
1355 e = doc.documentElement
1356 a1 = e.getAttributeNode("a1")
1357 a2 = e.getAttributeNode("a2")
1358 self.confirm(doc.getElementById("v") is None
1359 and not a1.isId
1360 and not a2.isId)
1361 e.setIdAttribute("a1")
1362 self.confirm(e.isSameNode(doc.getElementById("v"))
1363 and a1.isId
1364 and not a2.isId)
1365 e.setIdAttribute("a2")
1366 self.confirm(e.isSameNode(doc.getElementById("v"))
1367 and e.isSameNode(doc.getElementById("w"))
1368 and a1.isId
1369 and a2.isId)
1370 # replace the a1 node; the new node should *not* be an ID
1371 a3 = doc.createAttribute("a1")
1372 a3.value = "v"
1373 e.setAttributeNode(a3)
1374 self.confirm(doc.getElementById("v") is None
1375 and e.isSameNode(doc.getElementById("w"))
1376 and not a1.isId
1377 and a2.isId
1378 and not a3.isId)
1379 # renaming an attribute should not affect its ID-ness:
1380 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1381 self.confirm(e.isSameNode(doc.getElementById("w"))
1382 and a2.isId)
1383
1384 def testSetIdAttributeNS(self):
1385 NS1 = "http://xml.python.org/ns1"
1386 NS2 = "http://xml.python.org/ns2"
1387 doc = parseString("<doc"
1388 " xmlns:ns1='" + NS1 + "'"
1389 " xmlns:ns2='" + NS2 + "'"
1390 " ns1:a1='v' ns2:a2='w'/>")
1391 e = doc.documentElement
1392 a1 = e.getAttributeNodeNS(NS1, "a1")
1393 a2 = e.getAttributeNodeNS(NS2, "a2")
1394 self.confirm(doc.getElementById("v") is None
1395 and not a1.isId
1396 and not a2.isId)
1397 e.setIdAttributeNS(NS1, "a1")
1398 self.confirm(e.isSameNode(doc.getElementById("v"))
1399 and a1.isId
1400 and not a2.isId)
1401 e.setIdAttributeNS(NS2, "a2")
1402 self.confirm(e.isSameNode(doc.getElementById("v"))
1403 and e.isSameNode(doc.getElementById("w"))
1404 and a1.isId
1405 and a2.isId)
1406 # replace the a1 node; the new node should *not* be an ID
1407 a3 = doc.createAttributeNS(NS1, "a1")
1408 a3.value = "v"
1409 e.setAttributeNode(a3)
1410 self.confirm(e.isSameNode(doc.getElementById("w")))
1411 self.confirm(not a1.isId)
1412 self.confirm(a2.isId)
1413 self.confirm(not a3.isId)
1414 self.confirm(doc.getElementById("v") is None)
1415 # renaming an attribute should not affect its ID-ness:
1416 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1417 self.confirm(e.isSameNode(doc.getElementById("w"))
1418 and a2.isId)
1419
1420 def testSetIdAttributeNode(self):
1421 NS1 = "http://xml.python.org/ns1"
1422 NS2 = "http://xml.python.org/ns2"
1423 doc = parseString("<doc"
1424 " xmlns:ns1='" + NS1 + "'"
1425 " xmlns:ns2='" + NS2 + "'"
1426 " ns1:a1='v' ns2:a2='w'/>")
1427 e = doc.documentElement
1428 a1 = e.getAttributeNodeNS(NS1, "a1")
1429 a2 = e.getAttributeNodeNS(NS2, "a2")
1430 self.confirm(doc.getElementById("v") is None
1431 and not a1.isId
1432 and not a2.isId)
1433 e.setIdAttributeNode(a1)
1434 self.confirm(e.isSameNode(doc.getElementById("v"))
1435 and a1.isId
1436 and not a2.isId)
1437 e.setIdAttributeNode(a2)
1438 self.confirm(e.isSameNode(doc.getElementById("v"))
1439 and e.isSameNode(doc.getElementById("w"))
1440 and a1.isId
1441 and a2.isId)
1442 # replace the a1 node; the new node should *not* be an ID
1443 a3 = doc.createAttributeNS(NS1, "a1")
1444 a3.value = "v"
1445 e.setAttributeNode(a3)
1446 self.confirm(e.isSameNode(doc.getElementById("w")))
1447 self.confirm(not a1.isId)
1448 self.confirm(a2.isId)
1449 self.confirm(not a3.isId)
1450 self.confirm(doc.getElementById("v") is None)
1451 # renaming an attribute should not affect its ID-ness:
1452 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1453 self.confirm(e.isSameNode(doc.getElementById("w"))
1454 and a2.isId)
1455
1456 def testPickledDocument(self):
1457 doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n"
1458 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
1459 " 'http://xml.python.org/system' [\n"
1460 " <!ELEMENT e EMPTY>\n"
1461 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
1462 "]><doc attr='value'> text\n"
1463 "<?pi sample?> <!-- comment --> <e/> </doc>")
1464 s = pickle.dumps(doc)
1465 doc2 = pickle.loads(s)
1466 stack = [(doc, doc2)]
1467 while stack:
1468 n1, n2 = stack.pop()
1469 self.confirm(n1.nodeType == n2.nodeType
1470 and len(n1.childNodes) == len(n2.childNodes)
1471 and n1.nodeName == n2.nodeName
1472 and not n1.isSameNode(n2)
1473 and not n2.isSameNode(n1))
1474 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1475 len(n1.entities)
1476 len(n2.entities)
1477 len(n1.notations)
1478 len(n2.notations)
1479 self.confirm(len(n1.entities) == len(n2.entities)
1480 and len(n1.notations) == len(n2.notations))
1481 for i in range(len(n1.notations)):
Georg Brandl89fad142010-03-14 10:23:39 +00001482 # XXX this loop body doesn't seem to be executed?
Guido van Rossumd8faa362007-04-27 19:54:29 +00001483 no1 = n1.notations.item(i)
1484 no2 = n1.notations.item(i)
1485 self.confirm(no1.name == no2.name
1486 and no1.publicId == no2.publicId
1487 and no1.systemId == no2.systemId)
Georg Brandl89fad142010-03-14 10:23:39 +00001488 stack.append((no1, no2))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001489 for i in range(len(n1.entities)):
1490 e1 = n1.entities.item(i)
1491 e2 = n2.entities.item(i)
1492 self.confirm(e1.notationName == e2.notationName
1493 and e1.publicId == e2.publicId
1494 and e1.systemId == e2.systemId)
1495 stack.append((e1, e2))
1496 if n1.nodeType != Node.DOCUMENT_NODE:
1497 self.confirm(n1.ownerDocument.isSameNode(doc)
1498 and n2.ownerDocument.isSameNode(doc2))
1499 for i in range(len(n1.childNodes)):
1500 stack.append((n1.childNodes[i], n2.childNodes[i]))
1501
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001502 def testSerializeCommentNodeWithDoubleHyphen(self):
1503 doc = create_doc_without_doctype()
1504 doc.appendChild(doc.createComment("foo--bar"))
1505 self.assertRaises(ValueError, doc.toxml)
1506
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001507 def testEmptyXMLNSValue(self):
1508 doc = parseString("<element xmlns=''>\n"
1509 "<foo/>\n</element>")
1510 doc2 = parseString(doc.toxml())
1511 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1512
1513
Guido van Rossumd8faa362007-04-27 19:54:29 +00001514def test_main():
1515 run_unittest(MinidomTest)
1516
1517if __name__ == "__main__":
1518 test_main()