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