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