1# test for xml.dom.minidom 2 3import copy 4import pickle 5from StringIO import StringIO 6from test import support 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 = support.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 check_clone_node_entity(self, clone_document): 715 # bpo-35052: Test user data handler in cloneNode() on a document with 716 # an entity 717 document = xml.dom.minidom.parseString(""" 718 <?xml version="1.0" ?> 719 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 720 "http://www.w3.org/TR/html4/strict.dtd" 721 [ <!ENTITY smile ":-)"> ] 722 > 723 <doc>Don't let entities make you frown ⌣</doc> 724 """.strip()) 725 726 class Handler: 727 def handle(self, operation, key, data, src, dst): 728 self.operation = operation 729 self.key = key 730 self.data = data 731 self.src = src 732 self.dst = dst 733 734 handler = Handler() 735 doctype = document.doctype 736 entity = doctype.entities['smile'] 737 entity.setUserData("key", "data", handler) 738 739 if clone_document: 740 # clone Document 741 clone = document.cloneNode(deep=True) 742 743 self.assertEqual(clone.documentElement.firstChild.wholeText, 744 "Don't let entities make you frown :-)") 745 operation = xml.dom.UserDataHandler.NODE_IMPORTED 746 dst = clone.doctype.entities['smile'] 747 else: 748 # clone DocumentType 749 with support.swap_attr(doctype, 'ownerDocument', None): 750 clone = doctype.cloneNode(deep=True) 751 752 operation = xml.dom.UserDataHandler.NODE_CLONED 753 dst = clone.entities['smile'] 754 755 self.assertEqual(handler.operation, operation) 756 self.assertEqual(handler.key, "key") 757 self.assertEqual(handler.data, "data") 758 self.assertIs(handler.src, entity) 759 self.assertIs(handler.dst, dst) 760 761 def testCloneNodeEntity(self): 762 self.check_clone_node_entity(False) 763 self.check_clone_node_entity(True) 764 765 def testNormalize(self): 766 doc = parseString("<doc/>") 767 root = doc.documentElement 768 root.appendChild(doc.createTextNode("first")) 769 root.appendChild(doc.createTextNode("second")) 770 self.confirm(len(root.childNodes) == 2 771 and root.childNodes.length == 2, 772 "testNormalize -- preparation") 773 doc.normalize() 774 self.confirm(len(root.childNodes) == 1 775 and root.childNodes.length == 1 776 and root.firstChild is root.lastChild 777 and root.firstChild.data == "firstsecond" 778 , "testNormalize -- result") 779 doc.unlink() 780 781 doc = parseString("<doc/>") 782 root = doc.documentElement 783 root.appendChild(doc.createTextNode("")) 784 doc.normalize() 785 self.confirm(len(root.childNodes) == 0 786 and root.childNodes.length == 0, 787 "testNormalize -- single empty node removed") 788 doc.unlink() 789 790 def testNormalizeCombineAndNextSibling(self): 791 doc = parseString("<doc/>") 792 root = doc.documentElement 793 root.appendChild(doc.createTextNode("first")) 794 root.appendChild(doc.createTextNode("second")) 795 root.appendChild(doc.createElement("i")) 796 self.confirm(len(root.childNodes) == 3 797 and root.childNodes.length == 3, 798 "testNormalizeCombineAndNextSibling -- preparation") 799 doc.normalize() 800 self.confirm(len(root.childNodes) == 2 801 and root.childNodes.length == 2 802 and root.firstChild.data == "firstsecond" 803 and root.firstChild is not root.lastChild 804 and root.firstChild.nextSibling is root.lastChild 805 and root.firstChild.previousSibling is None 806 and root.lastChild.previousSibling is root.firstChild 807 and root.lastChild.nextSibling is None 808 , "testNormalizeCombinedAndNextSibling -- result") 809 doc.unlink() 810 811 def testNormalizeDeleteWithPrevSibling(self): 812 doc = parseString("<doc/>") 813 root = doc.documentElement 814 root.appendChild(doc.createTextNode("first")) 815 root.appendChild(doc.createTextNode("")) 816 self.confirm(len(root.childNodes) == 2 817 and root.childNodes.length == 2, 818 "testNormalizeDeleteWithPrevSibling -- preparation") 819 doc.normalize() 820 self.confirm(len(root.childNodes) == 1 821 and root.childNodes.length == 1 822 and root.firstChild.data == "first" 823 and root.firstChild is root.lastChild 824 and root.firstChild.nextSibling is None 825 and root.firstChild.previousSibling is None 826 , "testNormalizeDeleteWithPrevSibling -- result") 827 doc.unlink() 828 829 def testNormalizeDeleteWithNextSibling(self): 830 doc = parseString("<doc/>") 831 root = doc.documentElement 832 root.appendChild(doc.createTextNode("")) 833 root.appendChild(doc.createTextNode("second")) 834 self.confirm(len(root.childNodes) == 2 835 and root.childNodes.length == 2, 836 "testNormalizeDeleteWithNextSibling -- preparation") 837 doc.normalize() 838 self.confirm(len(root.childNodes) == 1 839 and root.childNodes.length == 1 840 and root.firstChild.data == "second" 841 and root.firstChild is root.lastChild 842 and root.firstChild.nextSibling is None 843 and root.firstChild.previousSibling is None 844 , "testNormalizeDeleteWithNextSibling -- result") 845 doc.unlink() 846 847 def testNormalizeDeleteWithTwoNonTextSiblings(self): 848 doc = parseString("<doc/>") 849 root = doc.documentElement 850 root.appendChild(doc.createElement("i")) 851 root.appendChild(doc.createTextNode("")) 852 root.appendChild(doc.createElement("i")) 853 self.confirm(len(root.childNodes) == 3 854 and root.childNodes.length == 3, 855 "testNormalizeDeleteWithTwoSiblings -- preparation") 856 doc.normalize() 857 self.confirm(len(root.childNodes) == 2 858 and root.childNodes.length == 2 859 and root.firstChild is not root.lastChild 860 and root.firstChild.nextSibling is root.lastChild 861 and root.firstChild.previousSibling is None 862 and root.lastChild.previousSibling is root.firstChild 863 and root.lastChild.nextSibling is None 864 , "testNormalizeDeleteWithTwoSiblings -- result") 865 doc.unlink() 866 867 def testNormalizeDeleteAndCombine(self): 868 doc = parseString("<doc/>") 869 root = doc.documentElement 870 root.appendChild(doc.createTextNode("")) 871 root.appendChild(doc.createTextNode("second")) 872 root.appendChild(doc.createTextNode("")) 873 root.appendChild(doc.createTextNode("fourth")) 874 root.appendChild(doc.createTextNode("")) 875 self.confirm(len(root.childNodes) == 5 876 and root.childNodes.length == 5, 877 "testNormalizeDeleteAndCombine -- preparation") 878 doc.normalize() 879 self.confirm(len(root.childNodes) == 1 880 and root.childNodes.length == 1 881 and root.firstChild is root.lastChild 882 and root.firstChild.data == "secondfourth" 883 and root.firstChild.previousSibling is None 884 and root.firstChild.nextSibling is None 885 , "testNormalizeDeleteAndCombine -- result") 886 doc.unlink() 887 888 def testNormalizeRecursion(self): 889 doc = parseString("<doc>" 890 "<o>" 891 "<i/>" 892 "t" 893 # 894 #x 895 "</o>" 896 "<o>" 897 "<o>" 898 "t2" 899 #x2 900 "</o>" 901 "t3" 902 #x3 903 "</o>" 904 # 905 "</doc>") 906 root = doc.documentElement 907 root.childNodes[0].appendChild(doc.createTextNode("")) 908 root.childNodes[0].appendChild(doc.createTextNode("x")) 909 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2")) 910 root.childNodes[1].appendChild(doc.createTextNode("x3")) 911 root.appendChild(doc.createTextNode("")) 912 self.confirm(len(root.childNodes) == 3 913 and root.childNodes.length == 3 914 and len(root.childNodes[0].childNodes) == 4 915 and root.childNodes[0].childNodes.length == 4 916 and len(root.childNodes[1].childNodes) == 3 917 and root.childNodes[1].childNodes.length == 3 918 and len(root.childNodes[1].childNodes[0].childNodes) == 2 919 and root.childNodes[1].childNodes[0].childNodes.length == 2 920 , "testNormalize2 -- preparation") 921 doc.normalize() 922 self.confirm(len(root.childNodes) == 2 923 and root.childNodes.length == 2 924 and len(root.childNodes[0].childNodes) == 2 925 and root.childNodes[0].childNodes.length == 2 926 and len(root.childNodes[1].childNodes) == 2 927 and root.childNodes[1].childNodes.length == 2 928 and len(root.childNodes[1].childNodes[0].childNodes) == 1 929 and root.childNodes[1].childNodes[0].childNodes.length == 1 930 , "testNormalize2 -- childNodes lengths") 931 self.confirm(root.childNodes[0].childNodes[1].data == "tx" 932 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2" 933 and root.childNodes[1].childNodes[1].data == "t3x3" 934 , "testNormalize2 -- joined text fields") 935 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None 936 and root.childNodes[0].childNodes[1].previousSibling 937 is root.childNodes[0].childNodes[0] 938 and root.childNodes[0].childNodes[0].previousSibling is None 939 and root.childNodes[0].childNodes[0].nextSibling 940 is root.childNodes[0].childNodes[1] 941 and root.childNodes[1].childNodes[1].nextSibling is None 942 and root.childNodes[1].childNodes[1].previousSibling 943 is root.childNodes[1].childNodes[0] 944 and root.childNodes[1].childNodes[0].previousSibling is None 945 and root.childNodes[1].childNodes[0].nextSibling 946 is root.childNodes[1].childNodes[1] 947 , "testNormalize2 -- sibling pointers") 948 doc.unlink() 949 950 951 def testBug0777884(self): 952 doc = parseString("<o>text</o>") 953 text = doc.documentElement.childNodes[0] 954 self.assertEqual(text.nodeType, Node.TEXT_NODE) 955 # Should run quietly, doing nothing. 956 text.normalize() 957 doc.unlink() 958 959 def testBug1433694(self): 960 doc = parseString("<o><i/>t</o>") 961 node = doc.documentElement 962 node.childNodes[1].nodeValue = "" 963 node.normalize() 964 self.confirm(node.childNodes[-1].nextSibling is None, 965 "Final child's .nextSibling should be None") 966 967 def testSiblings(self): 968 doc = parseString("<doc><?pi?>text?<elm/></doc>") 969 root = doc.documentElement 970 (pi, text, elm) = root.childNodes 971 972 self.confirm(pi.nextSibling is text and 973 pi.previousSibling is None and 974 text.nextSibling is elm and 975 text.previousSibling is pi and 976 elm.nextSibling is None and 977 elm.previousSibling is text, "testSiblings") 978 979 doc.unlink() 980 981 def testParents(self): 982 doc = parseString( 983 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>") 984 root = doc.documentElement 985 elm1 = root.childNodes[0] 986 (elm2a, elm2b) = elm1.childNodes 987 elm3 = elm2b.childNodes[0] 988 989 self.confirm(root.parentNode is doc and 990 elm1.parentNode is root and 991 elm2a.parentNode is elm1 and 992 elm2b.parentNode is elm1 and 993 elm3.parentNode is elm2b, "testParents") 994 doc.unlink() 995 996 def testNodeListItem(self): 997 doc = parseString("<doc><e/><e/></doc>") 998 children = doc.childNodes 999 docelem = children[0] 1000 self.confirm(children[0] is children.item(0) 1001 and children.item(1) is None 1002 and docelem.childNodes.item(0) is docelem.childNodes[0] 1003 and docelem.childNodes.item(1) is docelem.childNodes[1] 1004 and docelem.childNodes.item(0).childNodes.item(0) is None, 1005 "test NodeList.item()") 1006 doc.unlink() 1007 1008 def testSAX2DOM(self): 1009 from xml.dom import pulldom 1010 1011 sax2dom = pulldom.SAX2DOM() 1012 sax2dom.startDocument() 1013 sax2dom.startElement("doc", {}) 1014 sax2dom.characters("text") 1015 sax2dom.startElement("subelm", {}) 1016 sax2dom.characters("text") 1017 sax2dom.endElement("subelm") 1018 sax2dom.characters("text") 1019 sax2dom.endElement("doc") 1020 sax2dom.endDocument() 1021 1022 doc = sax2dom.document 1023 root = doc.documentElement 1024 (text1, elm1, text2) = root.childNodes 1025 text3 = elm1.childNodes[0] 1026 1027 self.confirm(text1.previousSibling is None and 1028 text1.nextSibling is elm1 and 1029 elm1.previousSibling is text1 and 1030 elm1.nextSibling is text2 and 1031 text2.previousSibling is elm1 and 1032 text2.nextSibling is None and 1033 text3.previousSibling is None and 1034 text3.nextSibling is None, "testSAX2DOM - siblings") 1035 1036 self.confirm(root.parentNode is doc and 1037 text1.parentNode is root and 1038 elm1.parentNode is root and 1039 text2.parentNode is root and 1040 text3.parentNode is elm1, "testSAX2DOM - parents") 1041 doc.unlink() 1042 1043 def testEncodings(self): 1044 doc = parseString('<foo>€</foo>') 1045 self.confirm(doc.toxml() == u'<?xml version="1.0" ?><foo>\u20ac</foo>' 1046 and doc.toxml('utf-8') == 1047 '<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>' 1048 and doc.toxml('iso-8859-15') == 1049 '<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>', 1050 "testEncodings - encoding EURO SIGN") 1051 1052 # Verify that character decoding errors raise exceptions instead 1053 # of crashing 1054 self.assertRaises(UnicodeDecodeError, parseString, 1055 '<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>') 1056 1057 doc.unlink() 1058 1059 class UserDataHandler: 1060 called = 0 1061 def handle(self, operation, key, data, src, dst): 1062 dst.setUserData(key, data + 1, self) 1063 src.setUserData(key, None, None) 1064 self.called = 1 1065 1066 def testUserData(self): 1067 dom = Document() 1068 n = dom.createElement('e') 1069 self.confirm(n.getUserData("foo") is None) 1070 n.setUserData("foo", None, None) 1071 self.confirm(n.getUserData("foo") is None) 1072 n.setUserData("foo", 12, 12) 1073 n.setUserData("bar", 13, 13) 1074 self.confirm(n.getUserData("foo") == 12) 1075 self.confirm(n.getUserData("bar") == 13) 1076 n.setUserData("foo", None, None) 1077 self.confirm(n.getUserData("foo") is None) 1078 self.confirm(n.getUserData("bar") == 13) 1079 1080 handler = self.UserDataHandler() 1081 n.setUserData("bar", 12, handler) 1082 c = n.cloneNode(1) 1083 self.confirm(handler.called 1084 and n.getUserData("bar") is None 1085 and c.getUserData("bar") == 13) 1086 n.unlink() 1087 c.unlink() 1088 dom.unlink() 1089 1090 def checkRenameNodeSharedConstraints(self, doc, node): 1091 # Make sure illegal NS usage is detected: 1092 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node, 1093 "http://xml.python.org/ns", "xmlns:foo") 1094 doc2 = parseString("<doc/>") 1095 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node, 1096 xml.dom.EMPTY_NAMESPACE, "foo") 1097 1098 def testRenameAttribute(self): 1099 doc = parseString("<doc a='v'/>") 1100 elem = doc.documentElement 1101 attrmap = elem.attributes 1102 attr = elem.attributes['a'] 1103 1104 # Simple renaming 1105 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") 1106 self.confirm(attr.name == "b" 1107 and attr.nodeName == "b" 1108 and attr.localName is None 1109 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE 1110 and attr.prefix is None 1111 and attr.value == "v" 1112 and elem.getAttributeNode("a") is None 1113 and elem.getAttributeNode("b").isSameNode(attr) 1114 and attrmap["b"].isSameNode(attr) 1115 and attr.ownerDocument.isSameNode(doc) 1116 and attr.ownerElement.isSameNode(elem)) 1117 1118 # Rename to have a namespace, no prefix 1119 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") 1120 self.confirm(attr.name == "c" 1121 and attr.nodeName == "c" 1122 and attr.localName == "c" 1123 and attr.namespaceURI == "http://xml.python.org/ns" 1124 and attr.prefix is None 1125 and attr.value == "v" 1126 and elem.getAttributeNode("a") is None 1127 and elem.getAttributeNode("b") is None 1128 and elem.getAttributeNode("c").isSameNode(attr) 1129 and elem.getAttributeNodeNS( 1130 "http://xml.python.org/ns", "c").isSameNode(attr) 1131 and attrmap["c"].isSameNode(attr) 1132 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) 1133 1134 # Rename to have a namespace, with prefix 1135 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") 1136 self.confirm(attr.name == "p:d" 1137 and attr.nodeName == "p:d" 1138 and attr.localName == "d" 1139 and attr.namespaceURI == "http://xml.python.org/ns2" 1140 and attr.prefix == "p" 1141 and attr.value == "v" 1142 and elem.getAttributeNode("a") is None 1143 and elem.getAttributeNode("b") is None 1144 and elem.getAttributeNode("c") is None 1145 and elem.getAttributeNodeNS( 1146 "http://xml.python.org/ns", "c") is None 1147 and elem.getAttributeNode("p:d").isSameNode(attr) 1148 and elem.getAttributeNodeNS( 1149 "http://xml.python.org/ns2", "d").isSameNode(attr) 1150 and attrmap["p:d"].isSameNode(attr) 1151 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) 1152 1153 # Rename back to a simple non-NS node 1154 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") 1155 self.confirm(attr.name == "e" 1156 and attr.nodeName == "e" 1157 and attr.localName is None 1158 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE 1159 and attr.prefix is None 1160 and attr.value == "v" 1161 and elem.getAttributeNode("a") is None 1162 and elem.getAttributeNode("b") is None 1163 and elem.getAttributeNode("c") is None 1164 and elem.getAttributeNode("p:d") is None 1165 and elem.getAttributeNodeNS( 1166 "http://xml.python.org/ns", "c") is None 1167 and elem.getAttributeNode("e").isSameNode(attr) 1168 and attrmap["e"].isSameNode(attr)) 1169 1170 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, 1171 "http://xml.python.org/ns", "xmlns") 1172 self.checkRenameNodeSharedConstraints(doc, attr) 1173 doc.unlink() 1174 1175 def testRenameElement(self): 1176 doc = parseString("<doc/>") 1177 elem = doc.documentElement 1178 1179 # Simple renaming 1180 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") 1181 self.confirm(elem.tagName == "a" 1182 and elem.nodeName == "a" 1183 and elem.localName is None 1184 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE 1185 and elem.prefix is None 1186 and elem.ownerDocument.isSameNode(doc)) 1187 1188 # Rename to have a namespace, no prefix 1189 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") 1190 self.confirm(elem.tagName == "b" 1191 and elem.nodeName == "b" 1192 and elem.localName == "b" 1193 and elem.namespaceURI == "http://xml.python.org/ns" 1194 and elem.prefix is None 1195 and elem.ownerDocument.isSameNode(doc)) 1196 1197 # Rename to have a namespace, with prefix 1198 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") 1199 self.confirm(elem.tagName == "p:c" 1200 and elem.nodeName == "p:c" 1201 and elem.localName == "c" 1202 and elem.namespaceURI == "http://xml.python.org/ns2" 1203 and elem.prefix == "p" 1204 and elem.ownerDocument.isSameNode(doc)) 1205 1206 # Rename back to a simple non-NS node 1207 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") 1208 self.confirm(elem.tagName == "d" 1209 and elem.nodeName == "d" 1210 and elem.localName is None 1211 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE 1212 and elem.prefix is None 1213 and elem.ownerDocument.isSameNode(doc)) 1214 1215 self.checkRenameNodeSharedConstraints(doc, elem) 1216 doc.unlink() 1217 1218 def testRenameOther(self): 1219 # We have to create a comment node explicitly since not all DOM 1220 # builders used with minidom add comments to the DOM. 1221 doc = xml.dom.minidom.getDOMImplementation().createDocument( 1222 xml.dom.EMPTY_NAMESPACE, "e", None) 1223 node = doc.createComment("comment") 1224 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node, 1225 xml.dom.EMPTY_NAMESPACE, "foo") 1226 doc.unlink() 1227 1228 def testWholeText(self): 1229 doc = parseString("<doc>a</doc>") 1230 elem = doc.documentElement 1231 text = elem.childNodes[0] 1232 self.assertEqual(text.nodeType, Node.TEXT_NODE) 1233 1234 self.checkWholeText(text, "a") 1235 elem.appendChild(doc.createTextNode("b")) 1236 self.checkWholeText(text, "ab") 1237 elem.insertBefore(doc.createCDATASection("c"), text) 1238 self.checkWholeText(text, "cab") 1239 1240 # make sure we don't cross other nodes 1241 splitter = doc.createComment("comment") 1242 elem.appendChild(splitter) 1243 text2 = doc.createTextNode("d") 1244 elem.appendChild(text2) 1245 self.checkWholeText(text, "cab") 1246 self.checkWholeText(text2, "d") 1247 1248 x = doc.createElement("x") 1249 elem.replaceChild(x, splitter) 1250 splitter = x 1251 self.checkWholeText(text, "cab") 1252 self.checkWholeText(text2, "d") 1253 1254 x = doc.createProcessingInstruction("y", "z") 1255 elem.replaceChild(x, splitter) 1256 splitter = x 1257 self.checkWholeText(text, "cab") 1258 self.checkWholeText(text2, "d") 1259 1260 elem.removeChild(splitter) 1261 self.checkWholeText(text, "cabd") 1262 self.checkWholeText(text2, "cabd") 1263 1264 def testPatch1094164(self): 1265 doc = parseString("<doc><e/></doc>") 1266 elem = doc.documentElement 1267 e = elem.firstChild 1268 self.confirm(e.parentNode is elem, "Before replaceChild()") 1269 # Check that replacing a child with itself leaves the tree unchanged 1270 elem.replaceChild(e, e) 1271 self.confirm(e.parentNode is elem, "After replaceChild()") 1272 1273 def testReplaceWholeText(self): 1274 def setup(): 1275 doc = parseString("<doc>a<e/>d</doc>") 1276 elem = doc.documentElement 1277 text1 = elem.firstChild 1278 text2 = elem.lastChild 1279 splitter = text1.nextSibling 1280 elem.insertBefore(doc.createTextNode("b"), splitter) 1281 elem.insertBefore(doc.createCDATASection("c"), text1) 1282 return doc, elem, text1, splitter, text2 1283 1284 doc, elem, text1, splitter, text2 = setup() 1285 text = text1.replaceWholeText("new content") 1286 self.checkWholeText(text, "new content") 1287 self.checkWholeText(text2, "d") 1288 self.confirm(len(elem.childNodes) == 3) 1289 1290 doc, elem, text1, splitter, text2 = setup() 1291 text = text2.replaceWholeText("new content") 1292 self.checkWholeText(text, "new content") 1293 self.checkWholeText(text1, "cab") 1294 self.confirm(len(elem.childNodes) == 5) 1295 1296 doc, elem, text1, splitter, text2 = setup() 1297 text = text1.replaceWholeText("") 1298 self.checkWholeText(text2, "d") 1299 self.confirm(text is None 1300 and len(elem.childNodes) == 2) 1301 1302 def testSchemaType(self): 1303 doc = parseString( 1304 "<!DOCTYPE doc [\n" 1305 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n" 1306 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n" 1307 " <!ATTLIST doc id ID #IMPLIED \n" 1308 " ref IDREF #IMPLIED \n" 1309 " refs IDREFS #IMPLIED \n" 1310 " enum (a|b) #IMPLIED \n" 1311 " ent ENTITY #IMPLIED \n" 1312 " ents ENTITIES #IMPLIED \n" 1313 " nm NMTOKEN #IMPLIED \n" 1314 " nms NMTOKENS #IMPLIED \n" 1315 " text CDATA #IMPLIED \n" 1316 " >\n" 1317 "]><doc id='name' notid='name' text='splat!' enum='b'" 1318 " ref='name' refs='name name' ent='e1' ents='e1 e2'" 1319 " nm='123' nms='123 abc' />") 1320 elem = doc.documentElement 1321 # We don't want to rely on any specific loader at this point, so 1322 # just make sure we can get to all the names, and that the 1323 # DTD-based namespace is right. The names can vary by loader 1324 # since each supports a different level of DTD information. 1325 t = elem.schemaType 1326 self.confirm(t.name is None 1327 and t.namespace == xml.dom.EMPTY_NAMESPACE) 1328 names = "id notid text enum ref refs ent ents nm nms".split() 1329 for name in names: 1330 a = elem.getAttributeNode(name) 1331 t = a.schemaType 1332 self.confirm(hasattr(t, "name") 1333 and t.namespace == xml.dom.EMPTY_NAMESPACE) 1334 1335 def testSetIdAttribute(self): 1336 doc = parseString("<doc a1='v' a2='w'/>") 1337 e = doc.documentElement 1338 a1 = e.getAttributeNode("a1") 1339 a2 = e.getAttributeNode("a2") 1340 self.confirm(doc.getElementById("v") is None 1341 and not a1.isId 1342 and not a2.isId) 1343 e.setIdAttribute("a1") 1344 self.confirm(e.isSameNode(doc.getElementById("v")) 1345 and a1.isId 1346 and not a2.isId) 1347 e.setIdAttribute("a2") 1348 self.confirm(e.isSameNode(doc.getElementById("v")) 1349 and e.isSameNode(doc.getElementById("w")) 1350 and a1.isId 1351 and a2.isId) 1352 # replace the a1 node; the new node should *not* be an ID 1353 a3 = doc.createAttribute("a1") 1354 a3.value = "v" 1355 e.setAttributeNode(a3) 1356 self.confirm(doc.getElementById("v") is None 1357 and e.isSameNode(doc.getElementById("w")) 1358 and not a1.isId 1359 and a2.isId 1360 and not a3.isId) 1361 # renaming an attribute should not affect its ID-ness: 1362 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1363 self.confirm(e.isSameNode(doc.getElementById("w")) 1364 and a2.isId) 1365 1366 def testSetIdAttributeNS(self): 1367 NS1 = "http://xml.python.org/ns1" 1368 NS2 = "http://xml.python.org/ns2" 1369 doc = parseString("<doc" 1370 " xmlns:ns1='" + NS1 + "'" 1371 " xmlns:ns2='" + NS2 + "'" 1372 " ns1:a1='v' ns2:a2='w'/>") 1373 e = doc.documentElement 1374 a1 = e.getAttributeNodeNS(NS1, "a1") 1375 a2 = e.getAttributeNodeNS(NS2, "a2") 1376 self.confirm(doc.getElementById("v") is None 1377 and not a1.isId 1378 and not a2.isId) 1379 e.setIdAttributeNS(NS1, "a1") 1380 self.confirm(e.isSameNode(doc.getElementById("v")) 1381 and a1.isId 1382 and not a2.isId) 1383 e.setIdAttributeNS(NS2, "a2") 1384 self.confirm(e.isSameNode(doc.getElementById("v")) 1385 and e.isSameNode(doc.getElementById("w")) 1386 and a1.isId 1387 and a2.isId) 1388 # replace the a1 node; the new node should *not* be an ID 1389 a3 = doc.createAttributeNS(NS1, "a1") 1390 a3.value = "v" 1391 e.setAttributeNode(a3) 1392 self.confirm(e.isSameNode(doc.getElementById("w"))) 1393 self.confirm(not a1.isId) 1394 self.confirm(a2.isId) 1395 self.confirm(not a3.isId) 1396 self.confirm(doc.getElementById("v") is None) 1397 # renaming an attribute should not affect its ID-ness: 1398 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1399 self.confirm(e.isSameNode(doc.getElementById("w")) 1400 and a2.isId) 1401 1402 def testSetIdAttributeNode(self): 1403 NS1 = "http://xml.python.org/ns1" 1404 NS2 = "http://xml.python.org/ns2" 1405 doc = parseString("<doc" 1406 " xmlns:ns1='" + NS1 + "'" 1407 " xmlns:ns2='" + NS2 + "'" 1408 " ns1:a1='v' ns2:a2='w'/>") 1409 e = doc.documentElement 1410 a1 = e.getAttributeNodeNS(NS1, "a1") 1411 a2 = e.getAttributeNodeNS(NS2, "a2") 1412 self.confirm(doc.getElementById("v") is None 1413 and not a1.isId 1414 and not a2.isId) 1415 e.setIdAttributeNode(a1) 1416 self.confirm(e.isSameNode(doc.getElementById("v")) 1417 and a1.isId 1418 and not a2.isId) 1419 e.setIdAttributeNode(a2) 1420 self.confirm(e.isSameNode(doc.getElementById("v")) 1421 and e.isSameNode(doc.getElementById("w")) 1422 and a1.isId 1423 and a2.isId) 1424 # replace the a1 node; the new node should *not* be an ID 1425 a3 = doc.createAttributeNS(NS1, "a1") 1426 a3.value = "v" 1427 e.setAttributeNode(a3) 1428 self.confirm(e.isSameNode(doc.getElementById("w"))) 1429 self.confirm(not a1.isId) 1430 self.confirm(a2.isId) 1431 self.confirm(not a3.isId) 1432 self.confirm(doc.getElementById("v") is None) 1433 # renaming an attribute should not affect its ID-ness: 1434 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") 1435 self.confirm(e.isSameNode(doc.getElementById("w")) 1436 and a2.isId) 1437 1438 def assert_recursive_equal(self, doc, doc2): 1439 stack = [(doc, doc2)] 1440 while stack: 1441 n1, n2 = stack.pop() 1442 self.assertEqual(n1.nodeType, n2.nodeType) 1443 self.assertEqual(len(n1.childNodes), len(n2.childNodes)) 1444 self.assertEqual(n1.nodeName, n2.nodeName) 1445 self.assertFalse(n1.isSameNode(n2)) 1446 self.assertFalse(n2.isSameNode(n1)) 1447 if n1.nodeType == Node.DOCUMENT_TYPE_NODE: 1448 len(n1.entities) 1449 len(n2.entities) 1450 len(n1.notations) 1451 len(n2.notations) 1452 self.assertEqual(len(n1.entities), len(n2.entities)) 1453 self.assertEqual(len(n1.notations), len(n2.notations)) 1454 for i in range(len(n1.notations)): 1455 # XXX this loop body doesn't seem to be executed? 1456 no1 = n1.notations.item(i) 1457 no2 = n1.notations.item(i) 1458 self.assertEqual(no1.name, no2.name) 1459 self.assertEqual(no1.publicId, no2.publicId) 1460 self.assertEqual(no1.systemId, no2.systemId) 1461 stack.append((no1, no2)) 1462 for i in range(len(n1.entities)): 1463 e1 = n1.entities.item(i) 1464 e2 = n2.entities.item(i) 1465 self.assertEqual(e1.notationName, e2.notationName) 1466 self.assertEqual(e1.publicId, e2.publicId) 1467 self.assertEqual(e1.systemId, e2.systemId) 1468 stack.append((e1, e2)) 1469 if n1.nodeType != Node.DOCUMENT_NODE: 1470 self.assertTrue(n1.ownerDocument.isSameNode(doc)) 1471 self.assertTrue(n2.ownerDocument.isSameNode(doc2)) 1472 for i in range(len(n1.childNodes)): 1473 stack.append((n1.childNodes[i], n2.childNodes[i])) 1474 1475 def testPickledDocument(self): 1476 doc = parseString(sample) 1477 for proto in range(pickle.HIGHEST_PROTOCOL + 1): 1478 s = pickle.dumps(doc, proto) 1479 doc2 = pickle.loads(s) 1480 self.assert_recursive_equal(doc, doc2) 1481 1482 def testDeepcopiedDocument(self): 1483 doc = parseString(sample) 1484 doc2 = copy.deepcopy(doc) 1485 self.assert_recursive_equal(doc, doc2) 1486 1487 def testSerializeCommentNodeWithDoubleHyphen(self): 1488 doc = create_doc_without_doctype() 1489 doc.appendChild(doc.createComment("foo--bar")) 1490 self.assertRaises(ValueError, doc.toxml) 1491 1492 def testEmptyXMLNSValue(self): 1493 doc = parseString("<element xmlns=''>\n" 1494 "<foo/>\n</element>") 1495 doc2 = parseString(doc.toxml()) 1496 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) 1497 1498 1499def test_main(): 1500 support.run_unittest(MinidomTest) 1501 1502if __name__ == "__main__": 1503 test_main() 1504