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