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