blob: 200b95db73dd2a825c0f733680341570b26c5894 [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
449 def testProcessingInstruction(self):
450 dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
451 pi = dom.documentElement.firstChild
452 self.confirm(pi.target == "mypi"
453 and pi.data == "data \t\n "
454 and pi.nodeName == "mypi"
455 and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE
456 and pi.attributes is None
457 and not pi.hasChildNodes()
458 and len(pi.childNodes) == 0
459 and pi.firstChild is None
460 and pi.lastChild is None
461 and pi.localName is None
462 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
463
464 def testProcessingInstructionRepr(self): pass
465
466 def testTextRepr(self): pass
467
468 def testWriteText(self): pass
469
470 def testDocumentElement(self): pass
471
472 def testTooManyDocumentElements(self):
473 doc = parseString("<doc/>")
474 elem = doc.createElement("extra")
475 # Should raise an exception when adding an extra document element.
476 self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem)
477 elem.unlink()
478 doc.unlink()
479
480 def testCreateElementNS(self): pass
481
482 def testCreateAttributeNS(self): pass
483
484 def testParse(self): pass
485
486 def testParseString(self): pass
487
488 def testComment(self): pass
489
490 def testAttrListItem(self): pass
491
492 def testAttrListItems(self): pass
493
494 def testAttrListItemNS(self): pass
495
496 def testAttrListKeys(self): pass
497
498 def testAttrListKeysNS(self): pass
499
500 def testRemoveNamedItem(self):
501 doc = parseString("<doc a=''/>")
502 e = doc.documentElement
503 attrs = e.attributes
504 a1 = e.getAttributeNode("a")
505 a2 = attrs.removeNamedItem("a")
506 self.confirm(a1.isSameNode(a2))
507 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a")
508
509 def testRemoveNamedItemNS(self):
510 doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>")
511 e = doc.documentElement
512 attrs = e.attributes
513 a1 = e.getAttributeNodeNS("http://xml.python.org/", "b")
514 a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b")
515 self.confirm(a1.isSameNode(a2))
516 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
517 "http://xml.python.org/", "b")
518
519 def testAttrListValues(self): pass
520
521 def testAttrListLength(self): pass
522
523 def testAttrList__getitem__(self): pass
524
525 def testAttrList__setitem__(self): pass
526
527 def testSetAttrValueandNodeValue(self): pass
528
529 def testParseElement(self): pass
530
531 def testParseAttributes(self): pass
532
533 def testParseElementNamespaces(self): pass
534
535 def testParseAttributeNamespaces(self): pass
536
537 def testParseProcessingInstructions(self): pass
538
539 def testChildNodes(self): pass
540
541 def testFirstChild(self): pass
542
543 def testHasChildNodes(self): pass
544
545 def _testCloneElementCopiesAttributes(self, e1, e2, test):
546 attrs1 = e1.attributes
547 attrs2 = e2.attributes
548 keys1 = list(attrs1.keys())
549 keys2 = list(attrs2.keys())
550 keys1.sort()
551 keys2.sort()
552 self.confirm(keys1 == keys2, "clone of element has same attribute keys")
553 for i in range(len(keys1)):
554 a1 = attrs1.item(i)
555 a2 = attrs2.item(i)
556 self.confirm(a1 is not a2
557 and a1.value == a2.value
558 and a1.nodeValue == a2.nodeValue
559 and a1.namespaceURI == a2.namespaceURI
560 and a1.localName == a2.localName
561 , "clone of attribute node has proper attribute values")
562 self.confirm(a2.ownerElement is e2,
563 "clone of attribute node correctly owned")
564
565 def _setupCloneElement(self, deep):
566 dom = parseString("<doc attr='value'><foo/></doc>")
567 root = dom.documentElement
568 clone = root.cloneNode(deep)
569 self._testCloneElementCopiesAttributes(
570 root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
571 # mutilate the original so shared data is detected
572 root.tagName = root.nodeName = "MODIFIED"
573 root.setAttribute("attr", "NEW VALUE")
574 root.setAttribute("added", "VALUE")
575 return dom, clone
576
577 def testCloneElementShallow(self):
578 dom, clone = self._setupCloneElement(0)
579 self.confirm(len(clone.childNodes) == 0
580 and clone.childNodes.length == 0
581 and clone.parentNode is None
582 and clone.toxml() == '<doc attr="value"/>'
583 , "testCloneElementShallow")
584 dom.unlink()
585
586 def testCloneElementDeep(self):
587 dom, clone = self._setupCloneElement(1)
588 self.confirm(len(clone.childNodes) == 1
589 and clone.childNodes.length == 1
590 and clone.parentNode is None
591 and clone.toxml() == '<doc attr="value"><foo/></doc>'
592 , "testCloneElementDeep")
593 dom.unlink()
594
595 def testCloneDocumentShallow(self):
596 doc = parseString("<?xml version='1.0'?>\n"
597 "<!-- comment -->"
598 "<!DOCTYPE doc [\n"
599 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
600 "]>\n"
601 "<doc attr='value'/>")
602 doc2 = doc.cloneNode(0)
603 self.confirm(doc2 is None,
604 "testCloneDocumentShallow:"
605 " shallow cloning of documents makes no sense!")
606
607 def testCloneDocumentDeep(self):
608 doc = parseString("<?xml version='1.0'?>\n"
609 "<!-- comment -->"
610 "<!DOCTYPE doc [\n"
611 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
612 "]>\n"
613 "<doc attr='value'/>")
614 doc2 = doc.cloneNode(1)
615 self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)),
616 "testCloneDocumentDeep: document objects not distinct")
617 self.confirm(len(doc.childNodes) == len(doc2.childNodes),
618 "testCloneDocumentDeep: wrong number of Document children")
619 self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE,
620 "testCloneDocumentDeep: documentElement not an ELEMENT_NODE")
621 self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2),
622 "testCloneDocumentDeep: documentElement owner is not new document")
623 self.confirm(not doc.documentElement.isSameNode(doc2.documentElement),
624 "testCloneDocumentDeep: documentElement should not be shared")
625 if doc.doctype is not None:
626 # check the doctype iff the original DOM maintained it
627 self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE,
628 "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE")
629 self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2))
630 self.confirm(not doc.doctype.isSameNode(doc2.doctype))
631
632 def testCloneDocumentTypeDeepOk(self):
633 doctype = create_nonempty_doctype()
634 clone = doctype.cloneNode(1)
635 self.confirm(clone is not None
636 and clone.nodeName == doctype.nodeName
637 and clone.name == doctype.name
638 and clone.publicId == doctype.publicId
639 and clone.systemId == doctype.systemId
640 and len(clone.entities) == len(doctype.entities)
641 and clone.entities.item(len(clone.entities)) is None
642 and len(clone.notations) == len(doctype.notations)
643 and clone.notations.item(len(clone.notations)) is None
644 and len(clone.childNodes) == 0)
645 for i in range(len(doctype.entities)):
646 se = doctype.entities.item(i)
647 ce = clone.entities.item(i)
648 self.confirm((not se.isSameNode(ce))
649 and (not ce.isSameNode(se))
650 and ce.nodeName == se.nodeName
651 and ce.notationName == se.notationName
652 and ce.publicId == se.publicId
653 and ce.systemId == se.systemId
654 and ce.encoding == se.encoding
655 and ce.actualEncoding == se.actualEncoding
656 and ce.version == se.version)
657 for i in range(len(doctype.notations)):
658 sn = doctype.notations.item(i)
659 cn = clone.notations.item(i)
660 self.confirm((not sn.isSameNode(cn))
661 and (not cn.isSameNode(sn))
662 and cn.nodeName == sn.nodeName
663 and cn.publicId == sn.publicId
664 and cn.systemId == sn.systemId)
665
666 def testCloneDocumentTypeDeepNotOk(self):
667 doc = create_doc_with_doctype()
668 clone = doc.doctype.cloneNode(1)
669 self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk")
670
671 def testCloneDocumentTypeShallowOk(self):
672 doctype = create_nonempty_doctype()
673 clone = doctype.cloneNode(0)
674 self.confirm(clone is not None
675 and clone.nodeName == doctype.nodeName
676 and clone.name == doctype.name
677 and clone.publicId == doctype.publicId
678 and clone.systemId == doctype.systemId
679 and len(clone.entities) == 0
680 and clone.entities.item(0) is None
681 and len(clone.notations) == 0
682 and clone.notations.item(0) is None
683 and len(clone.childNodes) == 0)
684
685 def testCloneDocumentTypeShallowNotOk(self):
686 doc = create_doc_with_doctype()
687 clone = doc.doctype.cloneNode(0)
688 self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk")
689
690 def check_import_document(self, deep, testName):
691 doc1 = parseString("<doc/>")
692 doc2 = parseString("<doc/>")
693 self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep)
694
695 def testImportDocumentShallow(self):
696 self.check_import_document(0, "testImportDocumentShallow")
697
698 def testImportDocumentDeep(self):
699 self.check_import_document(1, "testImportDocumentDeep")
700
701 def testImportDocumentTypeShallow(self):
702 src = create_doc_with_doctype()
703 target = create_doc_without_doctype()
704 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
705 src.doctype, 0)
706
707 def testImportDocumentTypeDeep(self):
708 src = create_doc_with_doctype()
709 target = create_doc_without_doctype()
710 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
711 src.doctype, 1)
712
713 # Testing attribute clones uses a helper, and should always be deep,
714 # even if the argument to cloneNode is false.
715 def check_clone_attribute(self, deep, testName):
716 doc = parseString("<doc attr='value'/>")
717 attr = doc.documentElement.getAttributeNode("attr")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000718 self.assertNotEqual(attr, None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000719 clone = attr.cloneNode(deep)
720 self.confirm(not clone.isSameNode(attr))
721 self.confirm(not attr.isSameNode(clone))
722 self.confirm(clone.ownerElement is None,
723 testName + ": ownerElement should be None")
724 self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument),
725 testName + ": ownerDocument does not match")
726 self.confirm(clone.specified,
727 testName + ": cloned attribute must have specified == True")
728
729 def testCloneAttributeShallow(self):
730 self.check_clone_attribute(0, "testCloneAttributeShallow")
731
732 def testCloneAttributeDeep(self):
733 self.check_clone_attribute(1, "testCloneAttributeDeep")
734
735 def check_clone_pi(self, deep, testName):
736 doc = parseString("<?target data?><doc/>")
737 pi = doc.firstChild
Ezio Melottib3aedd42010-11-20 19:04:17 +0000738 self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000739 clone = pi.cloneNode(deep)
740 self.confirm(clone.target == pi.target
741 and clone.data == pi.data)
742
743 def testClonePIShallow(self):
744 self.check_clone_pi(0, "testClonePIShallow")
745
746 def testClonePIDeep(self):
747 self.check_clone_pi(1, "testClonePIDeep")
748
749 def testNormalize(self):
750 doc = parseString("<doc/>")
751 root = doc.documentElement
752 root.appendChild(doc.createTextNode("first"))
753 root.appendChild(doc.createTextNode("second"))
754 self.confirm(len(root.childNodes) == 2
755 and root.childNodes.length == 2,
756 "testNormalize -- preparation")
757 doc.normalize()
758 self.confirm(len(root.childNodes) == 1
759 and root.childNodes.length == 1
760 and root.firstChild is root.lastChild
761 and root.firstChild.data == "firstsecond"
762 , "testNormalize -- result")
763 doc.unlink()
764
765 doc = parseString("<doc/>")
766 root = doc.documentElement
767 root.appendChild(doc.createTextNode(""))
768 doc.normalize()
769 self.confirm(len(root.childNodes) == 0
770 and root.childNodes.length == 0,
771 "testNormalize -- single empty node removed")
772 doc.unlink()
773
R. David Murraydc6da8a2009-04-09 22:16:43 +0000774 def testNormalizeCombineAndNextSibling(self):
775 doc = parseString("<doc/>")
776 root = doc.documentElement
777 root.appendChild(doc.createTextNode("first"))
778 root.appendChild(doc.createTextNode("second"))
779 root.appendChild(doc.createElement("i"))
780 self.confirm(len(root.childNodes) == 3
781 and root.childNodes.length == 3,
782 "testNormalizeCombineAndNextSibling -- preparation")
783 doc.normalize()
784 self.confirm(len(root.childNodes) == 2
785 and root.childNodes.length == 2
786 and root.firstChild.data == "firstsecond"
787 and root.firstChild is not root.lastChild
788 and root.firstChild.nextSibling is root.lastChild
789 and root.firstChild.previousSibling is None
790 and root.lastChild.previousSibling is root.firstChild
791 and root.lastChild.nextSibling is None
792 , "testNormalizeCombinedAndNextSibling -- result")
793 doc.unlink()
794
795 def testNormalizeDeleteWithPrevSibling(self):
796 doc = parseString("<doc/>")
797 root = doc.documentElement
798 root.appendChild(doc.createTextNode("first"))
799 root.appendChild(doc.createTextNode(""))
800 self.confirm(len(root.childNodes) == 2
801 and root.childNodes.length == 2,
802 "testNormalizeDeleteWithPrevSibling -- preparation")
803 doc.normalize()
804 self.confirm(len(root.childNodes) == 1
805 and root.childNodes.length == 1
806 and root.firstChild.data == "first"
807 and root.firstChild is root.lastChild
808 and root.firstChild.nextSibling is None
809 and root.firstChild.previousSibling is None
810 , "testNormalizeDeleteWithPrevSibling -- result")
811 doc.unlink()
812
813 def testNormalizeDeleteWithNextSibling(self):
814 doc = parseString("<doc/>")
815 root = doc.documentElement
816 root.appendChild(doc.createTextNode(""))
817 root.appendChild(doc.createTextNode("second"))
818 self.confirm(len(root.childNodes) == 2
819 and root.childNodes.length == 2,
820 "testNormalizeDeleteWithNextSibling -- preparation")
821 doc.normalize()
822 self.confirm(len(root.childNodes) == 1
823 and root.childNodes.length == 1
824 and root.firstChild.data == "second"
825 and root.firstChild is root.lastChild
826 and root.firstChild.nextSibling is None
827 and root.firstChild.previousSibling is None
828 , "testNormalizeDeleteWithNextSibling -- result")
829 doc.unlink()
830
831 def testNormalizeDeleteWithTwoNonTextSiblings(self):
832 doc = parseString("<doc/>")
833 root = doc.documentElement
834 root.appendChild(doc.createElement("i"))
835 root.appendChild(doc.createTextNode(""))
836 root.appendChild(doc.createElement("i"))
837 self.confirm(len(root.childNodes) == 3
838 and root.childNodes.length == 3,
839 "testNormalizeDeleteWithTwoSiblings -- preparation")
840 doc.normalize()
841 self.confirm(len(root.childNodes) == 2
842 and root.childNodes.length == 2
843 and root.firstChild is not root.lastChild
844 and root.firstChild.nextSibling is root.lastChild
845 and root.firstChild.previousSibling is None
846 and root.lastChild.previousSibling is root.firstChild
847 and root.lastChild.nextSibling is None
848 , "testNormalizeDeleteWithTwoSiblings -- result")
849 doc.unlink()
850
851 def testNormalizeDeleteAndCombine(self):
852 doc = parseString("<doc/>")
853 root = doc.documentElement
854 root.appendChild(doc.createTextNode(""))
855 root.appendChild(doc.createTextNode("second"))
856 root.appendChild(doc.createTextNode(""))
857 root.appendChild(doc.createTextNode("fourth"))
858 root.appendChild(doc.createTextNode(""))
859 self.confirm(len(root.childNodes) == 5
860 and root.childNodes.length == 5,
861 "testNormalizeDeleteAndCombine -- preparation")
862 doc.normalize()
863 self.confirm(len(root.childNodes) == 1
864 and root.childNodes.length == 1
865 and root.firstChild is root.lastChild
866 and root.firstChild.data == "secondfourth"
867 and root.firstChild.previousSibling is None
868 and root.firstChild.nextSibling is None
869 , "testNormalizeDeleteAndCombine -- result")
870 doc.unlink()
871
872 def testNormalizeRecursion(self):
873 doc = parseString("<doc>"
874 "<o>"
875 "<i/>"
876 "t"
877 #
878 #x
879 "</o>"
880 "<o>"
881 "<o>"
882 "t2"
883 #x2
884 "</o>"
885 "t3"
886 #x3
887 "</o>"
888 #
889 "</doc>")
890 root = doc.documentElement
891 root.childNodes[0].appendChild(doc.createTextNode(""))
892 root.childNodes[0].appendChild(doc.createTextNode("x"))
893 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
894 root.childNodes[1].appendChild(doc.createTextNode("x3"))
895 root.appendChild(doc.createTextNode(""))
896 self.confirm(len(root.childNodes) == 3
897 and root.childNodes.length == 3
898 and len(root.childNodes[0].childNodes) == 4
899 and root.childNodes[0].childNodes.length == 4
900 and len(root.childNodes[1].childNodes) == 3
901 and root.childNodes[1].childNodes.length == 3
902 and len(root.childNodes[1].childNodes[0].childNodes) == 2
903 and root.childNodes[1].childNodes[0].childNodes.length == 2
904 , "testNormalize2 -- preparation")
905 doc.normalize()
906 self.confirm(len(root.childNodes) == 2
907 and root.childNodes.length == 2
908 and len(root.childNodes[0].childNodes) == 2
909 and root.childNodes[0].childNodes.length == 2
910 and len(root.childNodes[1].childNodes) == 2
911 and root.childNodes[1].childNodes.length == 2
912 and len(root.childNodes[1].childNodes[0].childNodes) == 1
913 and root.childNodes[1].childNodes[0].childNodes.length == 1
914 , "testNormalize2 -- childNodes lengths")
915 self.confirm(root.childNodes[0].childNodes[1].data == "tx"
916 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
917 and root.childNodes[1].childNodes[1].data == "t3x3"
918 , "testNormalize2 -- joined text fields")
919 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
920 and root.childNodes[0].childNodes[1].previousSibling
921 is root.childNodes[0].childNodes[0]
922 and root.childNodes[0].childNodes[0].previousSibling is None
923 and root.childNodes[0].childNodes[0].nextSibling
924 is root.childNodes[0].childNodes[1]
925 and root.childNodes[1].childNodes[1].nextSibling is None
926 and root.childNodes[1].childNodes[1].previousSibling
927 is root.childNodes[1].childNodes[0]
928 and root.childNodes[1].childNodes[0].previousSibling is None
929 and root.childNodes[1].childNodes[0].nextSibling
930 is root.childNodes[1].childNodes[1]
931 , "testNormalize2 -- sibling pointers")
932 doc.unlink()
933
934
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +0000935 def testBug0777884(self):
936 doc = parseString("<o>text</o>")
937 text = doc.documentElement.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000938 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +0000939 # Should run quietly, doing nothing.
940 text.normalize()
941 doc.unlink()
942
Christian Heimes05e8be12008-02-23 18:30:17 +0000943 def testBug1433694(self):
944 doc = parseString("<o><i/>t</o>")
945 node = doc.documentElement
946 node.childNodes[1].nodeValue = ""
947 node.normalize()
Florent Xicluna9b86b9a2010-03-19 19:00:44 +0000948 self.confirm(node.childNodes[-1].nextSibling is None,
Christian Heimes05e8be12008-02-23 18:30:17 +0000949 "Final child's .nextSibling should be None")
950
Guido van Rossumd8faa362007-04-27 19:54:29 +0000951 def testSiblings(self):
952 doc = parseString("<doc><?pi?>text?<elm/></doc>")
953 root = doc.documentElement
954 (pi, text, elm) = root.childNodes
955
956 self.confirm(pi.nextSibling is text and
957 pi.previousSibling is None and
958 text.nextSibling is elm and
959 text.previousSibling is pi and
960 elm.nextSibling is None and
961 elm.previousSibling is text, "testSiblings")
962
963 doc.unlink()
964
965 def testParents(self):
966 doc = parseString(
967 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
968 root = doc.documentElement
969 elm1 = root.childNodes[0]
970 (elm2a, elm2b) = elm1.childNodes
971 elm3 = elm2b.childNodes[0]
972
973 self.confirm(root.parentNode is doc and
974 elm1.parentNode is root and
975 elm2a.parentNode is elm1 and
976 elm2b.parentNode is elm1 and
977 elm3.parentNode is elm2b, "testParents")
978 doc.unlink()
979
980 def testNodeListItem(self):
981 doc = parseString("<doc><e/><e/></doc>")
982 children = doc.childNodes
983 docelem = children[0]
984 self.confirm(children[0] is children.item(0)
985 and children.item(1) is None
986 and docelem.childNodes.item(0) is docelem.childNodes[0]
987 and docelem.childNodes.item(1) is docelem.childNodes[1]
988 and docelem.childNodes.item(0).childNodes.item(0) is None,
989 "test NodeList.item()")
990 doc.unlink()
991
992 def testSAX2DOM(self):
993 from xml.dom import pulldom
994
995 sax2dom = pulldom.SAX2DOM()
996 sax2dom.startDocument()
997 sax2dom.startElement("doc", {})
998 sax2dom.characters("text")
999 sax2dom.startElement("subelm", {})
1000 sax2dom.characters("text")
1001 sax2dom.endElement("subelm")
1002 sax2dom.characters("text")
1003 sax2dom.endElement("doc")
1004 sax2dom.endDocument()
1005
1006 doc = sax2dom.document
1007 root = doc.documentElement
1008 (text1, elm1, text2) = root.childNodes
1009 text3 = elm1.childNodes[0]
1010
1011 self.confirm(text1.previousSibling is None and
1012 text1.nextSibling is elm1 and
1013 elm1.previousSibling is text1 and
1014 elm1.nextSibling is text2 and
1015 text2.previousSibling is elm1 and
1016 text2.nextSibling is None and
1017 text3.previousSibling is None and
1018 text3.nextSibling is None, "testSAX2DOM - siblings")
1019
1020 self.confirm(root.parentNode is doc and
1021 text1.parentNode is root and
1022 elm1.parentNode is root and
1023 text2.parentNode is root and
1024 text3.parentNode is elm1, "testSAX2DOM - parents")
1025 doc.unlink()
1026
1027 def testEncodings(self):
1028 doc = parseString('<foo>&#x20ac;</foo>')
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001029 self.assertEqual(doc.toxml(),
1030 '<?xml version="1.0" ?><foo>\u20ac</foo>')
1031 self.assertEqual(doc.toxml('utf-8'),
1032 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>')
1033 self.assertEqual(doc.toxml('iso-8859-15'),
1034 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001035
1036 # Verify that character decoding errors throw exceptions instead
1037 # of crashing
1038 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001039 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001040
1041 doc.unlink()
1042
1043 class UserDataHandler:
1044 called = 0
1045 def handle(self, operation, key, data, src, dst):
1046 dst.setUserData(key, data + 1, self)
1047 src.setUserData(key, None, None)
1048 self.called = 1
1049
1050 def testUserData(self):
1051 dom = Document()
1052 n = dom.createElement('e')
1053 self.confirm(n.getUserData("foo") is None)
1054 n.setUserData("foo", None, None)
1055 self.confirm(n.getUserData("foo") is None)
1056 n.setUserData("foo", 12, 12)
1057 n.setUserData("bar", 13, 13)
1058 self.confirm(n.getUserData("foo") == 12)
1059 self.confirm(n.getUserData("bar") == 13)
1060 n.setUserData("foo", None, None)
1061 self.confirm(n.getUserData("foo") is None)
1062 self.confirm(n.getUserData("bar") == 13)
1063
1064 handler = self.UserDataHandler()
1065 n.setUserData("bar", 12, handler)
1066 c = n.cloneNode(1)
1067 self.confirm(handler.called
1068 and n.getUserData("bar") is None
1069 and c.getUserData("bar") == 13)
1070 n.unlink()
1071 c.unlink()
1072 dom.unlink()
1073
1074 def checkRenameNodeSharedConstraints(self, doc, node):
1075 # Make sure illegal NS usage is detected:
1076 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1077 "http://xml.python.org/ns", "xmlns:foo")
1078 doc2 = parseString("<doc/>")
1079 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1080 xml.dom.EMPTY_NAMESPACE, "foo")
1081
1082 def testRenameAttribute(self):
1083 doc = parseString("<doc a='v'/>")
1084 elem = doc.documentElement
1085 attrmap = elem.attributes
1086 attr = elem.attributes['a']
1087
1088 # Simple renaming
1089 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1090 self.confirm(attr.name == "b"
1091 and attr.nodeName == "b"
1092 and attr.localName is None
1093 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1094 and attr.prefix is None
1095 and attr.value == "v"
1096 and elem.getAttributeNode("a") is None
1097 and elem.getAttributeNode("b").isSameNode(attr)
1098 and attrmap["b"].isSameNode(attr)
1099 and attr.ownerDocument.isSameNode(doc)
1100 and attr.ownerElement.isSameNode(elem))
1101
1102 # Rename to have a namespace, no prefix
1103 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1104 self.confirm(attr.name == "c"
1105 and attr.nodeName == "c"
1106 and attr.localName == "c"
1107 and attr.namespaceURI == "http://xml.python.org/ns"
1108 and attr.prefix is None
1109 and attr.value == "v"
1110 and elem.getAttributeNode("a") is None
1111 and elem.getAttributeNode("b") is None
1112 and elem.getAttributeNode("c").isSameNode(attr)
1113 and elem.getAttributeNodeNS(
1114 "http://xml.python.org/ns", "c").isSameNode(attr)
1115 and attrmap["c"].isSameNode(attr)
1116 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1117
1118 # Rename to have a namespace, with prefix
1119 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1120 self.confirm(attr.name == "p:d"
1121 and attr.nodeName == "p:d"
1122 and attr.localName == "d"
1123 and attr.namespaceURI == "http://xml.python.org/ns2"
1124 and attr.prefix == "p"
1125 and attr.value == "v"
1126 and elem.getAttributeNode("a") is None
1127 and elem.getAttributeNode("b") is None
1128 and elem.getAttributeNode("c") is None
1129 and elem.getAttributeNodeNS(
1130 "http://xml.python.org/ns", "c") is None
1131 and elem.getAttributeNode("p:d").isSameNode(attr)
1132 and elem.getAttributeNodeNS(
1133 "http://xml.python.org/ns2", "d").isSameNode(attr)
1134 and attrmap["p:d"].isSameNode(attr)
1135 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1136
1137 # Rename back to a simple non-NS node
1138 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1139 self.confirm(attr.name == "e"
1140 and attr.nodeName == "e"
1141 and attr.localName is None
1142 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1143 and attr.prefix is None
1144 and attr.value == "v"
1145 and elem.getAttributeNode("a") is None
1146 and elem.getAttributeNode("b") is None
1147 and elem.getAttributeNode("c") is None
1148 and elem.getAttributeNode("p:d") is None
1149 and elem.getAttributeNodeNS(
1150 "http://xml.python.org/ns", "c") is None
1151 and elem.getAttributeNode("e").isSameNode(attr)
1152 and attrmap["e"].isSameNode(attr))
1153
1154 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1155 "http://xml.python.org/ns", "xmlns")
1156 self.checkRenameNodeSharedConstraints(doc, attr)
1157 doc.unlink()
1158
1159 def testRenameElement(self):
1160 doc = parseString("<doc/>")
1161 elem = doc.documentElement
1162
1163 # Simple renaming
1164 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1165 self.confirm(elem.tagName == "a"
1166 and elem.nodeName == "a"
1167 and elem.localName is None
1168 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1169 and elem.prefix is None
1170 and elem.ownerDocument.isSameNode(doc))
1171
1172 # Rename to have a namespace, no prefix
1173 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1174 self.confirm(elem.tagName == "b"
1175 and elem.nodeName == "b"
1176 and elem.localName == "b"
1177 and elem.namespaceURI == "http://xml.python.org/ns"
1178 and elem.prefix is None
1179 and elem.ownerDocument.isSameNode(doc))
1180
1181 # Rename to have a namespace, with prefix
1182 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1183 self.confirm(elem.tagName == "p:c"
1184 and elem.nodeName == "p:c"
1185 and elem.localName == "c"
1186 and elem.namespaceURI == "http://xml.python.org/ns2"
1187 and elem.prefix == "p"
1188 and elem.ownerDocument.isSameNode(doc))
1189
1190 # Rename back to a simple non-NS node
1191 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1192 self.confirm(elem.tagName == "d"
1193 and elem.nodeName == "d"
1194 and elem.localName is None
1195 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1196 and elem.prefix is None
1197 and elem.ownerDocument.isSameNode(doc))
1198
1199 self.checkRenameNodeSharedConstraints(doc, elem)
1200 doc.unlink()
1201
1202 def testRenameOther(self):
1203 # We have to create a comment node explicitly since not all DOM
1204 # builders used with minidom add comments to the DOM.
1205 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1206 xml.dom.EMPTY_NAMESPACE, "e", None)
1207 node = doc.createComment("comment")
1208 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1209 xml.dom.EMPTY_NAMESPACE, "foo")
1210 doc.unlink()
1211
1212 def testWholeText(self):
1213 doc = parseString("<doc>a</doc>")
1214 elem = doc.documentElement
1215 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001216 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001217
1218 self.checkWholeText(text, "a")
1219 elem.appendChild(doc.createTextNode("b"))
1220 self.checkWholeText(text, "ab")
1221 elem.insertBefore(doc.createCDATASection("c"), text)
1222 self.checkWholeText(text, "cab")
1223
1224 # make sure we don't cross other nodes
1225 splitter = doc.createComment("comment")
1226 elem.appendChild(splitter)
1227 text2 = doc.createTextNode("d")
1228 elem.appendChild(text2)
1229 self.checkWholeText(text, "cab")
1230 self.checkWholeText(text2, "d")
1231
1232 x = doc.createElement("x")
1233 elem.replaceChild(x, splitter)
1234 splitter = x
1235 self.checkWholeText(text, "cab")
1236 self.checkWholeText(text2, "d")
1237
1238 x = doc.createProcessingInstruction("y", "z")
1239 elem.replaceChild(x, splitter)
1240 splitter = x
1241 self.checkWholeText(text, "cab")
1242 self.checkWholeText(text2, "d")
1243
1244 elem.removeChild(splitter)
1245 self.checkWholeText(text, "cabd")
1246 self.checkWholeText(text2, "cabd")
1247
1248 def testPatch1094164(self):
1249 doc = parseString("<doc><e/></doc>")
1250 elem = doc.documentElement
1251 e = elem.firstChild
1252 self.confirm(e.parentNode is elem, "Before replaceChild()")
1253 # Check that replacing a child with itself leaves the tree unchanged
1254 elem.replaceChild(e, e)
1255 self.confirm(e.parentNode is elem, "After replaceChild()")
1256
1257 def testReplaceWholeText(self):
1258 def setup():
1259 doc = parseString("<doc>a<e/>d</doc>")
1260 elem = doc.documentElement
1261 text1 = elem.firstChild
1262 text2 = elem.lastChild
1263 splitter = text1.nextSibling
1264 elem.insertBefore(doc.createTextNode("b"), splitter)
1265 elem.insertBefore(doc.createCDATASection("c"), text1)
1266 return doc, elem, text1, splitter, text2
1267
1268 doc, elem, text1, splitter, text2 = setup()
1269 text = text1.replaceWholeText("new content")
1270 self.checkWholeText(text, "new content")
1271 self.checkWholeText(text2, "d")
1272 self.confirm(len(elem.childNodes) == 3)
1273
1274 doc, elem, text1, splitter, text2 = setup()
1275 text = text2.replaceWholeText("new content")
1276 self.checkWholeText(text, "new content")
1277 self.checkWholeText(text1, "cab")
1278 self.confirm(len(elem.childNodes) == 5)
1279
1280 doc, elem, text1, splitter, text2 = setup()
1281 text = text1.replaceWholeText("")
1282 self.checkWholeText(text2, "d")
1283 self.confirm(text is None
1284 and len(elem.childNodes) == 2)
1285
1286 def testSchemaType(self):
1287 doc = parseString(
1288 "<!DOCTYPE doc [\n"
1289 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1290 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1291 " <!ATTLIST doc id ID #IMPLIED \n"
1292 " ref IDREF #IMPLIED \n"
1293 " refs IDREFS #IMPLIED \n"
1294 " enum (a|b) #IMPLIED \n"
1295 " ent ENTITY #IMPLIED \n"
1296 " ents ENTITIES #IMPLIED \n"
1297 " nm NMTOKEN #IMPLIED \n"
1298 " nms NMTOKENS #IMPLIED \n"
1299 " text CDATA #IMPLIED \n"
1300 " >\n"
1301 "]><doc id='name' notid='name' text='splat!' enum='b'"
1302 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1303 " nm='123' nms='123 abc' />")
1304 elem = doc.documentElement
1305 # We don't want to rely on any specific loader at this point, so
1306 # just make sure we can get to all the names, and that the
1307 # DTD-based namespace is right. The names can vary by loader
1308 # since each supports a different level of DTD information.
1309 t = elem.schemaType
1310 self.confirm(t.name is None
1311 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1312 names = "id notid text enum ref refs ent ents nm nms".split()
1313 for name in names:
1314 a = elem.getAttributeNode(name)
1315 t = a.schemaType
1316 self.confirm(hasattr(t, "name")
1317 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1318
1319 def testSetIdAttribute(self):
1320 doc = parseString("<doc a1='v' a2='w'/>")
1321 e = doc.documentElement
1322 a1 = e.getAttributeNode("a1")
1323 a2 = e.getAttributeNode("a2")
1324 self.confirm(doc.getElementById("v") is None
1325 and not a1.isId
1326 and not a2.isId)
1327 e.setIdAttribute("a1")
1328 self.confirm(e.isSameNode(doc.getElementById("v"))
1329 and a1.isId
1330 and not a2.isId)
1331 e.setIdAttribute("a2")
1332 self.confirm(e.isSameNode(doc.getElementById("v"))
1333 and e.isSameNode(doc.getElementById("w"))
1334 and a1.isId
1335 and a2.isId)
1336 # replace the a1 node; the new node should *not* be an ID
1337 a3 = doc.createAttribute("a1")
1338 a3.value = "v"
1339 e.setAttributeNode(a3)
1340 self.confirm(doc.getElementById("v") is None
1341 and e.isSameNode(doc.getElementById("w"))
1342 and not a1.isId
1343 and a2.isId
1344 and not a3.isId)
1345 # renaming an attribute should not affect its ID-ness:
1346 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1347 self.confirm(e.isSameNode(doc.getElementById("w"))
1348 and a2.isId)
1349
1350 def testSetIdAttributeNS(self):
1351 NS1 = "http://xml.python.org/ns1"
1352 NS2 = "http://xml.python.org/ns2"
1353 doc = parseString("<doc"
1354 " xmlns:ns1='" + NS1 + "'"
1355 " xmlns:ns2='" + NS2 + "'"
1356 " ns1:a1='v' ns2:a2='w'/>")
1357 e = doc.documentElement
1358 a1 = e.getAttributeNodeNS(NS1, "a1")
1359 a2 = e.getAttributeNodeNS(NS2, "a2")
1360 self.confirm(doc.getElementById("v") is None
1361 and not a1.isId
1362 and not a2.isId)
1363 e.setIdAttributeNS(NS1, "a1")
1364 self.confirm(e.isSameNode(doc.getElementById("v"))
1365 and a1.isId
1366 and not a2.isId)
1367 e.setIdAttributeNS(NS2, "a2")
1368 self.confirm(e.isSameNode(doc.getElementById("v"))
1369 and e.isSameNode(doc.getElementById("w"))
1370 and a1.isId
1371 and a2.isId)
1372 # replace the a1 node; the new node should *not* be an ID
1373 a3 = doc.createAttributeNS(NS1, "a1")
1374 a3.value = "v"
1375 e.setAttributeNode(a3)
1376 self.confirm(e.isSameNode(doc.getElementById("w")))
1377 self.confirm(not a1.isId)
1378 self.confirm(a2.isId)
1379 self.confirm(not a3.isId)
1380 self.confirm(doc.getElementById("v") is None)
1381 # renaming an attribute should not affect its ID-ness:
1382 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1383 self.confirm(e.isSameNode(doc.getElementById("w"))
1384 and a2.isId)
1385
1386 def testSetIdAttributeNode(self):
1387 NS1 = "http://xml.python.org/ns1"
1388 NS2 = "http://xml.python.org/ns2"
1389 doc = parseString("<doc"
1390 " xmlns:ns1='" + NS1 + "'"
1391 " xmlns:ns2='" + NS2 + "'"
1392 " ns1:a1='v' ns2:a2='w'/>")
1393 e = doc.documentElement
1394 a1 = e.getAttributeNodeNS(NS1, "a1")
1395 a2 = e.getAttributeNodeNS(NS2, "a2")
1396 self.confirm(doc.getElementById("v") is None
1397 and not a1.isId
1398 and not a2.isId)
1399 e.setIdAttributeNode(a1)
1400 self.confirm(e.isSameNode(doc.getElementById("v"))
1401 and a1.isId
1402 and not a2.isId)
1403 e.setIdAttributeNode(a2)
1404 self.confirm(e.isSameNode(doc.getElementById("v"))
1405 and e.isSameNode(doc.getElementById("w"))
1406 and a1.isId
1407 and a2.isId)
1408 # replace the a1 node; the new node should *not* be an ID
1409 a3 = doc.createAttributeNS(NS1, "a1")
1410 a3.value = "v"
1411 e.setAttributeNode(a3)
1412 self.confirm(e.isSameNode(doc.getElementById("w")))
1413 self.confirm(not a1.isId)
1414 self.confirm(a2.isId)
1415 self.confirm(not a3.isId)
1416 self.confirm(doc.getElementById("v") is None)
1417 # renaming an attribute should not affect its ID-ness:
1418 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1419 self.confirm(e.isSameNode(doc.getElementById("w"))
1420 and a2.isId)
1421
1422 def testPickledDocument(self):
1423 doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n"
1424 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
1425 " 'http://xml.python.org/system' [\n"
1426 " <!ELEMENT e EMPTY>\n"
1427 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
1428 "]><doc attr='value'> text\n"
1429 "<?pi sample?> <!-- comment --> <e/> </doc>")
1430 s = pickle.dumps(doc)
1431 doc2 = pickle.loads(s)
1432 stack = [(doc, doc2)]
1433 while stack:
1434 n1, n2 = stack.pop()
1435 self.confirm(n1.nodeType == n2.nodeType
1436 and len(n1.childNodes) == len(n2.childNodes)
1437 and n1.nodeName == n2.nodeName
1438 and not n1.isSameNode(n2)
1439 and not n2.isSameNode(n1))
1440 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1441 len(n1.entities)
1442 len(n2.entities)
1443 len(n1.notations)
1444 len(n2.notations)
1445 self.confirm(len(n1.entities) == len(n2.entities)
1446 and len(n1.notations) == len(n2.notations))
1447 for i in range(len(n1.notations)):
Georg Brandl89fad142010-03-14 10:23:39 +00001448 # XXX this loop body doesn't seem to be executed?
Guido van Rossumd8faa362007-04-27 19:54:29 +00001449 no1 = n1.notations.item(i)
1450 no2 = n1.notations.item(i)
1451 self.confirm(no1.name == no2.name
1452 and no1.publicId == no2.publicId
1453 and no1.systemId == no2.systemId)
Georg Brandl89fad142010-03-14 10:23:39 +00001454 stack.append((no1, no2))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001455 for i in range(len(n1.entities)):
1456 e1 = n1.entities.item(i)
1457 e2 = n2.entities.item(i)
1458 self.confirm(e1.notationName == e2.notationName
1459 and e1.publicId == e2.publicId
1460 and e1.systemId == e2.systemId)
1461 stack.append((e1, e2))
1462 if n1.nodeType != Node.DOCUMENT_NODE:
1463 self.confirm(n1.ownerDocument.isSameNode(doc)
1464 and n2.ownerDocument.isSameNode(doc2))
1465 for i in range(len(n1.childNodes)):
1466 stack.append((n1.childNodes[i], n2.childNodes[i]))
1467
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001468 def testSerializeCommentNodeWithDoubleHyphen(self):
1469 doc = create_doc_without_doctype()
1470 doc.appendChild(doc.createComment("foo--bar"))
1471 self.assertRaises(ValueError, doc.toxml)
1472
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001473 def testEmptyXMLNSValue(self):
1474 doc = parseString("<element xmlns=''>\n"
1475 "<foo/>\n</element>")
1476 doc2 = parseString(doc.toxml())
1477 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1478
1479
Guido van Rossumd8faa362007-04-27 19:54:29 +00001480def test_main():
1481 run_unittest(MinidomTest)
1482
1483if __name__ == "__main__":
1484 test_main()