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