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