python (3.12.0)

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