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