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