Home | History | Annotate | Download | only in test
      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>&#x20ac;</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