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