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