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