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