# test for xml.dom.minidom import pickle from StringIO import StringIO from test.test_support import verbose, run_unittest, findfile import unittest import xml.dom import xml.dom.minidom import xml.parsers.expat from xml.dom.minidom import parse, Node, Document, parseString from xml.dom.minidom import getDOMImplementation tstfile = findfile("test.xml", subdir="xmltestdata") # The tests of DocumentType importing use these helpers to construct # the documents to work with, since not all DOM builders actually # create the DocumentType nodes. def create_doc_without_doctype(doctype=None): return getDOMImplementation().createDocument(None, "doc", doctype) def create_nonempty_doctype(): doctype = getDOMImplementation().createDocumentType("doc", None, None) doctype.entities._seq = [] doctype.notations._seq = [] notation = xml.dom.minidom.Notation("my-notation", None, "http://xml.python.org/notations/my") doctype.notations._seq.append(notation) entity = xml.dom.minidom.Entity("my-entity", None, "http://xml.python.org/entities/my", "my-notation") entity.version = "1.0" entity.encoding = "utf-8" entity.actualEncoding = "us-ascii" doctype.entities._seq.append(entity) return doctype def create_doc_with_doctype(): doctype = create_nonempty_doctype() doc = create_doc_without_doctype(doctype) doctype.entities.item(0).ownerDocument = doc doctype.notations.item(0).ownerDocument = doc return doc class MinidomTest(unittest.TestCase): def confirm(self, test, testname = "Test"): self.assertTrue(test, testname) def checkWholeText(self, node, s): t = node.wholeText self.confirm(t == s, "looking for %s, found %s" % (repr(s), repr(t))) def testParseFromFile(self): dom = parse(StringIO(open(tstfile).read())) dom.unlink() self.confirm(isinstance(dom,Document)) def testGetElementsByTagName(self): dom = parse(tstfile) self.confirm(dom.getElementsByTagName("LI") == \ dom.documentElement.getElementsByTagName("LI")) dom.unlink() def testInsertBefore(self): dom = parseString("") root = dom.documentElement elem = root.childNodes[0] nelem = dom.createElement("element") root.insertBefore(nelem, elem) self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2 and root.childNodes[0] is nelem and root.childNodes.item(0) is nelem and root.childNodes[1] is elem and root.childNodes.item(1) is elem and root.firstChild is nelem and root.lastChild is elem and root.toxml() == "" , "testInsertBefore -- node properly placed in tree") nelem = dom.createElement("element") root.insertBefore(nelem, None) self.confirm(len(root.childNodes) == 3 and root.childNodes.length == 3 and root.childNodes[1] is elem and root.childNodes.item(1) is elem and root.childNodes[2] is nelem and root.childNodes.item(2) is nelem and root.lastChild is nelem and nelem.previousSibling is elem and root.toxml() == "" , "testInsertBefore -- node properly placed in tree") nelem2 = dom.createElement("bar") root.insertBefore(nelem2, nelem) self.confirm(len(root.childNodes) == 4 and root.childNodes.length == 4 and root.childNodes[2] is nelem2 and root.childNodes.item(2) is nelem2 and root.childNodes[3] is nelem and root.childNodes.item(3) is nelem and nelem2.nextSibling is nelem and nelem.previousSibling is nelem2 and root.toxml() == "" , "testInsertBefore -- node properly placed in tree") dom.unlink() def _create_fragment_test_nodes(self): dom = parseString("") orig = dom.createTextNode("original") c1 = dom.createTextNode("foo") c2 = dom.createTextNode("bar") c3 = dom.createTextNode("bat") dom.documentElement.appendChild(orig) frag = dom.createDocumentFragment() frag.appendChild(c1) frag.appendChild(c2) frag.appendChild(c3) return dom, orig, c1, c2, c3, frag def testInsertBeforeFragment(self): dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() dom.documentElement.insertBefore(frag, None) self.confirm(tuple(dom.documentElement.childNodes) == (orig, c1, c2, c3), "insertBefore(, None)") frag.unlink() dom.unlink() dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() dom.documentElement.insertBefore(frag, orig) self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3, orig), "insertBefore(, orig)") frag.unlink() dom.unlink() def testAppendChild(self): dom = parse(tstfile) dom.documentElement.appendChild(dom.createComment(u"Hello")) self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment") self.confirm(dom.documentElement.childNodes[-1].data == "Hello") dom.unlink() def testAppendChildFragment(self): dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() dom.documentElement.appendChild(frag) self.confirm(tuple(dom.documentElement.childNodes) == (orig, c1, c2, c3), "appendChild()") frag.unlink() dom.unlink() def testReplaceChildFragment(self): dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes() dom.documentElement.replaceChild(frag, orig) orig.unlink() self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3), "replaceChild()") frag.unlink() dom.unlink() def testLegalChildren(self): dom = Document() elem = dom.createElement('element') text = dom.createTextNode('text') self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text) dom.appendChild(elem) self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text, elem) self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text, elem) nodemap = elem.attributes self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem, text) self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS, text) elem.appendChild(text) dom.unlink() def testNamedNodeMapSetItem(self): dom = Document() elem = dom.createElement('element') attrs = elem.attributes attrs["foo"] = "bar" a = attrs.item(0) self.confirm(a.ownerDocument is dom, "NamedNodeMap.__setitem__() sets ownerDocument") self.confirm(a.ownerElement is elem, "NamedNodeMap.__setitem__() sets ownerElement") self.confirm(a.value == "bar", "NamedNodeMap.__setitem__() sets value") self.confirm(a.nodeValue == "bar", "NamedNodeMap.__setitem__() sets nodeValue") elem.unlink() dom.unlink() def testNonZero(self): dom = parse(tstfile) self.confirm(dom)# should not be zero dom.appendChild(dom.createComment("foo")) self.confirm(not dom.childNodes[-1].childNodes) dom.unlink() def testUnlink(self): dom = parse(tstfile) dom.unlink() def testElement(self): dom = Document() dom.appendChild(dom.createElement("abc")) self.confirm(dom.documentElement) dom.unlink() def testAAA(self): dom = parseString("") el = dom.documentElement el.setAttribute("spam", "jam2") self.confirm(el.toxml() == '', "testAAA") a = el.getAttributeNode("spam") self.confirm(a.ownerDocument is dom, "setAttribute() sets ownerDocument") self.confirm(a.ownerElement is dom.documentElement, "setAttribute() sets ownerElement") dom.unlink() def testAAB(self): dom = parseString("") el = dom.documentElement el.setAttribute("spam", "jam") el.setAttribute("spam", "jam2") self.confirm(el.toxml() == '', "testAAB") dom.unlink() def testAddAttr(self): dom = Document() child = dom.appendChild(dom.createElement("abc")) child.setAttribute("def", "ghi") self.confirm(child.getAttribute("def") == "ghi") self.confirm(child.attributes["def"].value == "ghi") child.setAttribute("jkl", "mno") self.confirm(child.getAttribute("jkl") == "mno") self.confirm(child.attributes["jkl"].value == "mno") self.confirm(len(child.attributes) == 2) child.setAttribute("def", "newval") self.confirm(child.getAttribute("def") == "newval") self.confirm(child.attributes["def"].value == "newval") self.confirm(len(child.attributes) == 2) dom.unlink() def testDeleteAttr(self): dom = Document() child = dom.appendChild(dom.createElement("abc")) self.confirm(len(child.attributes) == 0) child.setAttribute("def", "ghi") self.confirm(len(child.attributes) == 1) del child.attributes["def"] self.confirm(len(child.attributes) == 0) dom.unlink() def testRemoveAttr(self): dom = Document() child = dom.appendChild(dom.createElement("abc")) child.setAttribute("def", "ghi") self.confirm(len(child.attributes) == 1) child.removeAttribute("def") self.confirm(len(child.attributes) == 0) dom.unlink() def testRemoveAttrNS(self): dom = Document() child = dom.appendChild( dom.createElementNS("http://www.python.org", "python:abc")) child.setAttributeNS("http://www.w3.org", "xmlns:python", "http://www.python.org") child.setAttributeNS("http://www.python.org", "python:abcattr", "foo") self.confirm(len(child.attributes) == 2) child.removeAttributeNS("http://www.python.org", "abcattr") self.confirm(len(child.attributes) == 1) dom.unlink() def testRemoveAttributeNode(self): dom = Document() child = dom.appendChild(dom.createElement("foo")) child.setAttribute("spam", "jam") self.confirm(len(child.attributes) == 1) node = child.getAttributeNode("spam") child.removeAttributeNode(node) self.confirm(len(child.attributes) == 0 and child.getAttributeNode("spam") is None) dom.unlink() def testChangeAttr(self): dom = parseString("") el = dom.documentElement el.setAttribute("spam", "jam") self.confirm(len(el.attributes) == 1) el.setAttribute("spam", "bam") # Set this attribute to be an ID and make sure that doesn't change # when changing the value: el.setIdAttribute("spam") self.confirm(len(el.attributes) == 1 and el.attributes["spam"].value == "bam" and el.attributes["spam"].nodeValue == "bam" and el.getAttribute("spam") == "bam" and el.getAttributeNode("spam").isId) el.attributes["spam"] = "ham" self.confirm(len(el.attributes) == 1 and el.attributes["spam"].value == "ham" and el.attributes["spam"].nodeValue == "ham" and el.getAttribute("spam") == "ham" and el.attributes["spam"].isId) el.setAttribute("spam2", "bam") self.confirm(len(el.attributes) == 2 and el.attributes["spam"].value == "ham" and el.attributes["spam"].nodeValue == "ham" and el.getAttribute("spam") == "ham" and el.attributes["spam2"].value == "bam" and el.attributes["spam2"].nodeValue == "bam" and el.getAttribute("spam2") == "bam") el.attributes["spam2"] = "bam2" self.confirm(len(el.attributes) == 2 and el.attributes["spam"].value == "ham" and el.attributes["spam"].nodeValue == "ham" and el.getAttribute("spam") == "ham" and el.attributes["spam2"].value == "bam2" and el.attributes["spam2"].nodeValue == "bam2" and el.getAttribute("spam2") == "bam2") dom.unlink() def testGetElementsByTagNameNS(self): d=""" """ dom = parseString(d) elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom", "myelem") self.confirm(len(elems) == 1 and elems[0].namespaceURI == "http://pyxml.sf.net/minidom" and elems[0].localName == "myelem" and elems[0].prefix == "minidom" and elems[0].tagName == "minidom:myelem" and elems[0].nodeName == "minidom:myelem") dom.unlink() def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri, lname): nodelist = doc.getElementsByTagNameNS(nsuri, lname) self.confirm(len(nodelist) == 0) def testGetEmptyNodeListFromElementsByTagNameNS(self): doc = parseString('') self.get_empty_nodelist_from_elements_by_tagName_ns_helper( doc, 'http://xml.python.org/namespaces/a', 'localname') self.get_empty_nodelist_from_elements_by_tagName_ns_helper( doc, '*', 'splat') self.get_empty_nodelist_from_elements_by_tagName_ns_helper( doc, 'http://xml.python.org/namespaces/a', '*') doc = parseString('') self.get_empty_nodelist_from_elements_by_tagName_ns_helper( doc, "http://xml.python.org/splat", "not-there") self.get_empty_nodelist_from_elements_by_tagName_ns_helper( doc, "*", "not-there") self.get_empty_nodelist_from_elements_by_tagName_ns_helper( doc, "http://somewhere.else.net/not-there", "e") def testElementReprAndStr(self): dom = Document() el = dom.appendChild(dom.createElement("abc")) string1 = repr(el) string2 = str(el) self.confirm(string1 == string2) dom.unlink() def testElementReprAndStrUnicode(self): dom = Document() el = dom.appendChild(dom.createElement(u"abc")) string1 = repr(el) string2 = str(el) self.confirm(string1 == string2) dom.unlink() def testElementReprAndStrUnicodeNS(self): dom = Document() el = dom.appendChild( dom.createElementNS(u"http://www.slashdot.org", u"slash:abc")) string1 = repr(el) string2 = str(el) self.confirm(string1 == string2) self.confirm("slash:abc" in string1) dom.unlink() def testAttributeRepr(self): dom = Document() el = dom.appendChild(dom.createElement(u"abc")) node = el.setAttribute("abc", "def") self.confirm(str(node) == repr(node)) dom.unlink() def testWriteXML(self): str = '' dom = parseString(str) domstr = dom.toxml() dom.unlink() self.confirm(str == domstr) def testAltNewline(self): str = '\n\n' dom = parseString(str) domstr = dom.toprettyxml(newl="\r\n") dom.unlink() self.confirm(domstr == str.replace("\n", "\r\n")) def test_toprettyxml_with_text_nodes(self): # see issue #4147, text nodes are not indented decl = '\n' self.assertEqual(parseString('A').toprettyxml(), decl + 'A\n') self.assertEqual(parseString('AA').toprettyxml(), decl + '\n\tA\n\tA\n\n') self.assertEqual(parseString('AA').toprettyxml(), decl + '\n\tA\n\tA\n\n') self.assertEqual(parseString('AA').toprettyxml(), decl + '\n\tA\n\tA\n\n') self.assertEqual(parseString('AAA').toprettyxml(), decl + '\n\tA\n\tA\n\tA\n\n') def test_toprettyxml_with_adjacent_text_nodes(self): # see issue #4147, adjacent text nodes are indented normally dom = Document() elem = dom.createElement(u'elem') elem.appendChild(dom.createTextNode(u'TEXT')) elem.appendChild(dom.createTextNode(u'TEXT')) dom.appendChild(elem) decl = '\n' self.assertEqual(dom.toprettyxml(), decl + '\n\tTEXT\n\tTEXT\n\n') def test_toprettyxml_preserves_content_of_text_node(self): # see issue #4147 for str in ('A', 'C'): dom = parseString(str) dom2 = parseString(dom.toprettyxml()) self.assertEqual( dom.getElementsByTagName('B')[0].childNodes[0].toxml(), dom2.getElementsByTagName('B')[0].childNodes[0].toxml()) def testProcessingInstruction(self): dom = parseString('') pi = dom.documentElement.firstChild self.confirm(pi.target == "mypi" and pi.data == "data \t\n " and pi.nodeName == "mypi" and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE and pi.attributes is None and not pi.hasChildNodes() and len(pi.childNodes) == 0 and pi.firstChild is None and pi.lastChild is None and pi.localName is None and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE) def testTooManyDocumentElements(self): doc = parseString("") elem = doc.createElement("extra") # Should raise an exception when adding an extra document element. self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem) elem.unlink() doc.unlink() def testRemoveNamedItem(self): doc = parseString("") e = doc.documentElement attrs = e.attributes a1 = e.getAttributeNode("a") a2 = attrs.removeNamedItem("a") self.confirm(a1.isSameNode(a2)) self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a") def testRemoveNamedItemNS(self): doc = parseString("") e = doc.documentElement attrs = e.attributes a1 = e.getAttributeNodeNS("http://xml.python.org/", "b") a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b") self.confirm(a1.isSameNode(a2)) self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS, "http://xml.python.org/", "b") def _testCloneElementCopiesAttributes(self, e1, e2, test): attrs1 = e1.attributes attrs2 = e2.attributes keys1 = attrs1.keys() keys2 = attrs2.keys() keys1.sort() keys2.sort() self.confirm(keys1 == keys2, "clone of element has same attribute keys") for i in range(len(keys1)): a1 = attrs1.item(i) a2 = attrs2.item(i) self.confirm(a1 is not a2 and a1.value == a2.value and a1.nodeValue == a2.nodeValue and a1.namespaceURI == a2.namespaceURI and a1.localName == a2.localName , "clone of attribute node has proper attribute values") self.confirm(a2.ownerElement is e2, "clone of attribute node correctly owned") def _setupCloneElement(self, deep): dom = parseString("") root = dom.documentElement clone = root.cloneNode(deep) self._testCloneElementCopiesAttributes( root, clone, "testCloneElement" + (deep and "Deep" or "Shallow")) # mutilate the original so shared data is detected root.tagName = root.nodeName = "MODIFIED" root.setAttribute("attr", "NEW VALUE") root.setAttribute("added", "VALUE") return dom, clone def testCloneElementShallow(self): dom, clone = self._setupCloneElement(0) self.confirm(len(clone.childNodes) == 0 and clone.childNodes.length == 0 and clone.parentNode is None and clone.toxml() == '' , "testCloneElementShallow") dom.unlink() def testCloneElementDeep(self): dom, clone = self._setupCloneElement(1) self.confirm(len(clone.childNodes) == 1 and clone.childNodes.length == 1 and clone.parentNode is None and clone.toxml() == '' , "testCloneElementDeep") dom.unlink() def testCloneDocumentShallow(self): doc = parseString("\n" "" "\n" "]>\n" "") doc2 = doc.cloneNode(0) self.confirm(doc2 is None, "testCloneDocumentShallow:" " shallow cloning of documents makes no sense!") def testCloneDocumentDeep(self): doc = parseString("\n" "" "\n" "]>\n" "") doc2 = doc.cloneNode(1) self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)), "testCloneDocumentDeep: document objects not distinct") self.confirm(len(doc.childNodes) == len(doc2.childNodes), "testCloneDocumentDeep: wrong number of Document children") self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE, "testCloneDocumentDeep: documentElement not an ELEMENT_NODE") self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2), "testCloneDocumentDeep: documentElement owner is not new document") self.confirm(not doc.documentElement.isSameNode(doc2.documentElement), "testCloneDocumentDeep: documentElement should not be shared") if doc.doctype is not None: # check the doctype iff the original DOM maintained it self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE, "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE") self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2)) self.confirm(not doc.doctype.isSameNode(doc2.doctype)) def testCloneDocumentTypeDeepOk(self): doctype = create_nonempty_doctype() clone = doctype.cloneNode(1) self.confirm(clone is not None and clone.nodeName == doctype.nodeName and clone.name == doctype.name and clone.publicId == doctype.publicId and clone.systemId == doctype.systemId and len(clone.entities) == len(doctype.entities) and clone.entities.item(len(clone.entities)) is None and len(clone.notations) == len(doctype.notations) and clone.notations.item(len(clone.notations)) is None and len(clone.childNodes) == 0) for i in range(len(doctype.entities)): se = doctype.entities.item(i) ce = clone.entities.item(i) self.confirm((not se.isSameNode(ce)) and (not ce.isSameNode(se)) and ce.nodeName == se.nodeName and ce.notationName == se.notationName and ce.publicId == se.publicId and ce.systemId == se.systemId and ce.encoding == se.encoding and ce.actualEncoding == se.actualEncoding and ce.version == se.version) for i in range(len(doctype.notations)): sn = doctype.notations.item(i) cn = clone.notations.item(i) self.confirm((not sn.isSameNode(cn)) and (not cn.isSameNode(sn)) and cn.nodeName == sn.nodeName and cn.publicId == sn.publicId and cn.systemId == sn.systemId) def testCloneDocumentTypeDeepNotOk(self): doc = create_doc_with_doctype() clone = doc.doctype.cloneNode(1) self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk") def testCloneDocumentTypeShallowOk(self): doctype = create_nonempty_doctype() clone = doctype.cloneNode(0) self.confirm(clone is not None and clone.nodeName == doctype.nodeName and clone.name == doctype.name and clone.publicId == doctype.publicId and clone.systemId == doctype.systemId and len(clone.entities) == 0 and clone.entities.item(0) is None and len(clone.notations) == 0 and clone.notations.item(0) is None and len(clone.childNodes) == 0) def testCloneDocumentTypeShallowNotOk(self): doc = create_doc_with_doctype() clone = doc.doctype.cloneNode(0) self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk") def check_import_document(self, deep, testName): doc1 = parseString("") doc2 = parseString("") self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep) def testImportDocumentShallow(self): self.check_import_document(0, "testImportDocumentShallow") def testImportDocumentDeep(self): self.check_import_document(1, "testImportDocumentDeep") def testImportDocumentTypeShallow(self): src = create_doc_with_doctype() target = create_doc_without_doctype() self.assertRaises(xml.dom.NotSupportedErr, target.importNode, src.doctype, 0) def testImportDocumentTypeDeep(self): src = create_doc_with_doctype() target = create_doc_without_doctype() self.assertRaises(xml.dom.NotSupportedErr, target.importNode, src.doctype, 1) # Testing attribute clones uses a helper, and should always be deep, # even if the argument to cloneNode is false. def check_clone_attribute(self, deep, testName): doc = parseString("") attr = doc.documentElement.getAttributeNode("attr") self.assertNotEqual(attr, None) clone = attr.cloneNode(deep) self.confirm(not clone.isSameNode(attr)) self.confirm(not attr.isSameNode(clone)) self.confirm(clone.ownerElement is None, testName + ": ownerElement should be None") self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument), testName + ": ownerDocument does not match") self.confirm(clone.specified, testName + ": cloned attribute must have specified == True") def testCloneAttributeShallow(self): self.check_clone_attribute(0, "testCloneAttributeShallow") def testCloneAttributeDeep(self): self.check_clone_attribute(1, "testCloneAttributeDeep") def check_clone_pi(self, deep, testName): doc = parseString("") pi = doc.firstChild self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE) clone = pi.cloneNode(deep) self.confirm(clone.target == pi.target and clone.data == pi.data) def testClonePIShallow(self): self.check_clone_pi(0, "testClonePIShallow") def testClonePIDeep(self): self.check_clone_pi(1, "testClonePIDeep") def testNormalize(self): doc = parseString("") root = doc.documentElement root.appendChild(doc.createTextNode("first")) root.appendChild(doc.createTextNode("second")) self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2, "testNormalize -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 1 and root.childNodes.length == 1 and root.firstChild is root.lastChild and root.firstChild.data == "firstsecond" , "testNormalize -- result") doc.unlink() doc = parseString("") root = doc.documentElement root.appendChild(doc.createTextNode("")) doc.normalize() self.confirm(len(root.childNodes) == 0 and root.childNodes.length == 0, "testNormalize -- single empty node removed") doc.unlink() def testNormalizeCombineAndNextSibling(self): doc = parseString("") root = doc.documentElement root.appendChild(doc.createTextNode("first")) root.appendChild(doc.createTextNode("second")) root.appendChild(doc.createElement("i")) self.confirm(len(root.childNodes) == 3 and root.childNodes.length == 3, "testNormalizeCombineAndNextSibling -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2 and root.firstChild.data == "firstsecond" and root.firstChild is not root.lastChild and root.firstChild.nextSibling is root.lastChild and root.firstChild.previousSibling is None and root.lastChild.previousSibling is root.firstChild and root.lastChild.nextSibling is None , "testNormalizeCombinedAndNextSibling -- result") doc.unlink() def testNormalizeDeleteWithPrevSibling(self): doc = parseString("") root = doc.documentElement root.appendChild(doc.createTextNode("first")) root.appendChild(doc.createTextNode("")) self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2, "testNormalizeDeleteWithPrevSibling -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 1 and root.childNodes.length == 1 and root.firstChild.data == "first" and root.firstChild is root.lastChild and root.firstChild.nextSibling is None and root.firstChild.previousSibling is None , "testNormalizeDeleteWithPrevSibling -- result") doc.unlink() def testNormalizeDeleteWithNextSibling(self): doc = parseString("") root = doc.documentElement root.appendChild(doc.createTextNode("")) root.appendChild(doc.createTextNode("second")) self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2, "testNormalizeDeleteWithNextSibling -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 1 and root.childNodes.length == 1 and root.firstChild.data == "second" and root.firstChild is root.lastChild and root.firstChild.nextSibling is None and root.firstChild.previousSibling is None , "testNormalizeDeleteWithNextSibling -- result") doc.unlink() def testNormalizeDeleteWithTwoNonTextSiblings(self): doc = parseString("") root = doc.documentElement root.appendChild(doc.createElement("i")) root.appendChild(doc.createTextNode("")) root.appendChild(doc.createElement("i")) self.confirm(len(root.childNodes) == 3 and root.childNodes.length == 3, "testNormalizeDeleteWithTwoSiblings -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2 and root.firstChild is not root.lastChild and root.firstChild.nextSibling is root.lastChild and root.firstChild.previousSibling is None and root.lastChild.previousSibling is root.firstChild and root.lastChild.nextSibling is None , "testNormalizeDeleteWithTwoSiblings -- result") doc.unlink() def testNormalizeDeleteAndCombine(self): doc = parseString("") root = doc.documentElement root.appendChild(doc.createTextNode("")) root.appendChild(doc.createTextNode("second")) root.appendChild(doc.createTextNode("")) root.appendChild(doc.createTextNode("fourth")) root.appendChild(doc.createTextNode("")) self.confirm(len(root.childNodes) == 5 and root.childNodes.length == 5, "testNormalizeDeleteAndCombine -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 1 and root.childNodes.length == 1 and root.firstChild is root.lastChild and root.firstChild.data == "secondfourth" and root.firstChild.previousSibling is None and root.firstChild.nextSibling is None , "testNormalizeDeleteAndCombine -- result") doc.unlink() def testNormalizeRecursion(self): doc = parseString("" "" "" "t" # #x "" "" "" "t2" #x2 "" "t3" #x3 "" # "") root = doc.documentElement root.childNodes[0].appendChild(doc.createTextNode("")) root.childNodes[0].appendChild(doc.createTextNode("x")) root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2")) root.childNodes[1].appendChild(doc.createTextNode("x3")) root.appendChild(doc.createTextNode("")) self.confirm(len(root.childNodes) == 3 and root.childNodes.length == 3 and len(root.childNodes[0].childNodes) == 4 and root.childNodes[0].childNodes.length == 4 and len(root.childNodes[1].childNodes) == 3 and root.childNodes[1].childNodes.length == 3 and len(root.childNodes[1].childNodes[0].childNodes) == 2 and root.childNodes[1].childNodes[0].childNodes.length == 2 , "testNormalize2 -- preparation") doc.normalize() self.confirm(len(root.childNodes) == 2 and root.childNodes.length == 2 and len(root.childNodes[0].childNodes) == 2 and root.childNodes[0].childNodes.length == 2 and len(root.childNodes[1].childNodes) == 2 and root.childNodes[1].childNodes.length == 2 and len(root.childNodes[1].childNodes[0].childNodes) == 1 and root.childNodes[1].childNodes[0].childNodes.length == 1 , "testNormalize2 -- childNodes lengths") self.confirm(root.childNodes[0].childNodes[1].data == "tx" and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2" and root.childNodes[1].childNodes[1].data == "t3x3" , "testNormalize2 -- joined text fields") self.confirm(root.childNodes[0].childNodes[1].nextSibling is None and root.childNodes[0].childNodes[1].previousSibling is root.childNodes[0].childNodes[0] and root.childNodes[0].childNodes[0].previousSibling is None and root.childNodes[0].childNodes[0].nextSibling is root.childNodes[0].childNodes[1] and root.childNodes[1].childNodes[1].nextSibling is None and root.childNodes[1].childNodes[1].previousSibling is root.childNodes[1].childNodes[0] and root.childNodes[1].childNodes[0].previousSibling is None and root.childNodes[1].childNodes[0].nextSibling is root.childNodes[1].childNodes[1] , "testNormalize2 -- sibling pointers") doc.unlink() def testBug0777884(self): doc = parseString("text") text = doc.documentElement.childNodes[0] self.assertEqual(text.nodeType, Node.TEXT_NODE) # Should run quietly, doing nothing. text.normalize() doc.unlink() def testBug1433694(self): doc = parseString("t") node = doc.documentElement node.childNodes[1].nodeValue = "" node.normalize() self.confirm(node.childNodes[-1].nextSibling is None, "Final child's .nextSibling should be None") def testSiblings(self): doc = parseString("text?") root = doc.documentElement (pi, text, elm) = root.childNodes self.confirm(pi.nextSibling is text and pi.previousSibling is None and text.nextSibling is elm and text.previousSibling is pi and elm.nextSibling is None and elm.previousSibling is text, "testSiblings") doc.unlink() def testParents(self): doc = parseString( "") root = doc.documentElement elm1 = root.childNodes[0] (elm2a, elm2b) = elm1.childNodes elm3 = elm2b.childNodes[0] self.confirm(root.parentNode is doc and elm1.parentNode is root and elm2a.parentNode is elm1 and elm2b.parentNode is elm1 and elm3.parentNode is elm2b, "testParents") doc.unlink() def testNodeListItem(self): doc = parseString("") children = doc.childNodes docelem = children[0] self.confirm(children[0] is children.item(0) and children.item(1) is None and docelem.childNodes.item(0) is docelem.childNodes[0] and docelem.childNodes.item(1) is docelem.childNodes[1] and docelem.childNodes.item(0).childNodes.item(0) is None, "test NodeList.item()") doc.unlink() def testSAX2DOM(self): from xml.dom import pulldom sax2dom = pulldom.SAX2DOM() sax2dom.startDocument() sax2dom.startElement("doc", {}) sax2dom.characters("text") sax2dom.startElement("subelm", {}) sax2dom.characters("text") sax2dom.endElement("subelm") sax2dom.characters("text") sax2dom.endElement("doc") sax2dom.endDocument() doc = sax2dom.document root = doc.documentElement (text1, elm1, text2) = root.childNodes text3 = elm1.childNodes[0] self.confirm(text1.previousSibling is None and text1.nextSibling is elm1 and elm1.previousSibling is text1 and elm1.nextSibling is text2 and text2.previousSibling is elm1 and text2.nextSibling is None and text3.previousSibling is None and text3.nextSibling is None, "testSAX2DOM - siblings") self.confirm(root.parentNode is doc and text1.parentNode is root and elm1.parentNode is root and text2.parentNode is root and text3.parentNode is elm1, "testSAX2DOM - parents") doc.unlink() def testEncodings(self): doc = parseString('') self.confirm(doc.toxml() == u'\u20ac' and doc.toxml('utf-8') == '\xe2\x82\xac' and doc.toxml('iso-8859-15') == '\xa4', "testEncodings - encoding EURO SIGN") # Verify that character decoding errors raise exceptions instead # of crashing self.assertRaises(UnicodeDecodeError, parseString, 'Comment \xe7a va ? Tr\xe8s bien ?') doc.unlink() class UserDataHandler: called = 0 def handle(self, operation, key, data, src, dst): dst.setUserData(key, data + 1, self) src.setUserData(key, None, None) self.called = 1 def testUserData(self): dom = Document() n = dom.createElement('e') self.confirm(n.getUserData("foo") is None) n.setUserData("foo", None, None) self.confirm(n.getUserData("foo") is None) n.setUserData("foo", 12, 12) n.setUserData("bar", 13, 13) self.confirm(n.getUserData("foo") == 12) self.confirm(n.getUserData("bar") == 13) n.setUserData("foo", None, None) self.confirm(n.getUserData("foo") is None) self.confirm(n.getUserData("bar") == 13) handler = self.UserDataHandler() n.setUserData("bar", 12, handler) c = n.cloneNode(1) self.confirm(handler.called and n.getUserData("bar") is None and c.getUserData("bar") == 13) n.unlink() c.unlink() dom.unlink() def checkRenameNodeSharedConstraints(self, doc, node): # Make sure illegal NS usage is detected: self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node, "http://xml.python.org/ns", "xmlns:foo") doc2 = parseString("") self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node, xml.dom.EMPTY_NAMESPACE, "foo") def testRenameAttribute(self): doc = parseString("") elem = doc.documentElement attrmap = elem.attributes attr = elem.attributes['a'] # Simple renaming attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b") self.confirm(attr.name == "b" and attr.nodeName == "b" and attr.localName is None and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE and attr.prefix is None and attr.value == "v" and elem.getAttributeNode("a") is None and elem.getAttributeNode("b").isSameNode(attr) and attrmap["b"].isSameNode(attr) and attr.ownerDocument.isSameNode(doc) and attr.ownerElement.isSameNode(elem)) # Rename to have a namespace, no prefix attr = doc.renameNode(attr, "http://xml.python.org/ns", "c") self.confirm(attr.name == "c" and attr.nodeName == "c" and attr.localName == "c" and attr.namespaceURI == "http://xml.python.org/ns" and attr.prefix is None and attr.value == "v" and elem.getAttributeNode("a") is None and elem.getAttributeNode("b") is None and elem.getAttributeNode("c").isSameNode(attr) and elem.getAttributeNodeNS( "http://xml.python.org/ns", "c").isSameNode(attr) and attrmap["c"].isSameNode(attr) and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr)) # Rename to have a namespace, with prefix attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d") self.confirm(attr.name == "p:d" and attr.nodeName == "p:d" and attr.localName == "d" and attr.namespaceURI == "http://xml.python.org/ns2" and attr.prefix == "p" and attr.value == "v" and elem.getAttributeNode("a") is None and elem.getAttributeNode("b") is None and elem.getAttributeNode("c") is None and elem.getAttributeNodeNS( "http://xml.python.org/ns", "c") is None and elem.getAttributeNode("p:d").isSameNode(attr) and elem.getAttributeNodeNS( "http://xml.python.org/ns2", "d").isSameNode(attr) and attrmap["p:d"].isSameNode(attr) and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr)) # Rename back to a simple non-NS node attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e") self.confirm(attr.name == "e" and attr.nodeName == "e" and attr.localName is None and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE and attr.prefix is None and attr.value == "v" and elem.getAttributeNode("a") is None and elem.getAttributeNode("b") is None and elem.getAttributeNode("c") is None and elem.getAttributeNode("p:d") is None and elem.getAttributeNodeNS( "http://xml.python.org/ns", "c") is None and elem.getAttributeNode("e").isSameNode(attr) and attrmap["e"].isSameNode(attr)) self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr, "http://xml.python.org/ns", "xmlns") self.checkRenameNodeSharedConstraints(doc, attr) doc.unlink() def testRenameElement(self): doc = parseString("") elem = doc.documentElement # Simple renaming elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a") self.confirm(elem.tagName == "a" and elem.nodeName == "a" and elem.localName is None and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE and elem.prefix is None and elem.ownerDocument.isSameNode(doc)) # Rename to have a namespace, no prefix elem = doc.renameNode(elem, "http://xml.python.org/ns", "b") self.confirm(elem.tagName == "b" and elem.nodeName == "b" and elem.localName == "b" and elem.namespaceURI == "http://xml.python.org/ns" and elem.prefix is None and elem.ownerDocument.isSameNode(doc)) # Rename to have a namespace, with prefix elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c") self.confirm(elem.tagName == "p:c" and elem.nodeName == "p:c" and elem.localName == "c" and elem.namespaceURI == "http://xml.python.org/ns2" and elem.prefix == "p" and elem.ownerDocument.isSameNode(doc)) # Rename back to a simple non-NS node elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d") self.confirm(elem.tagName == "d" and elem.nodeName == "d" and elem.localName is None and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE and elem.prefix is None and elem.ownerDocument.isSameNode(doc)) self.checkRenameNodeSharedConstraints(doc, elem) doc.unlink() def testRenameOther(self): # We have to create a comment node explicitly since not all DOM # builders used with minidom add comments to the DOM. doc = xml.dom.minidom.getDOMImplementation().createDocument( xml.dom.EMPTY_NAMESPACE, "e", None) node = doc.createComment("comment") self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node, xml.dom.EMPTY_NAMESPACE, "foo") doc.unlink() def testWholeText(self): doc = parseString("a") elem = doc.documentElement text = elem.childNodes[0] self.assertEqual(text.nodeType, Node.TEXT_NODE) self.checkWholeText(text, "a") elem.appendChild(doc.createTextNode("b")) self.checkWholeText(text, "ab") elem.insertBefore(doc.createCDATASection("c"), text) self.checkWholeText(text, "cab") # make sure we don't cross other nodes splitter = doc.createComment("comment") elem.appendChild(splitter) text2 = doc.createTextNode("d") elem.appendChild(text2) self.checkWholeText(text, "cab") self.checkWholeText(text2, "d") x = doc.createElement("x") elem.replaceChild(x, splitter) splitter = x self.checkWholeText(text, "cab") self.checkWholeText(text2, "d") x = doc.createProcessingInstruction("y", "z") elem.replaceChild(x, splitter) splitter = x self.checkWholeText(text, "cab") self.checkWholeText(text2, "d") elem.removeChild(splitter) self.checkWholeText(text, "cabd") self.checkWholeText(text2, "cabd") def testPatch1094164(self): doc = parseString("") elem = doc.documentElement e = elem.firstChild self.confirm(e.parentNode is elem, "Before replaceChild()") # Check that replacing a child with itself leaves the tree unchanged elem.replaceChild(e, e) self.confirm(e.parentNode is elem, "After replaceChild()") def testReplaceWholeText(self): def setup(): doc = parseString("ad") elem = doc.documentElement text1 = elem.firstChild text2 = elem.lastChild splitter = text1.nextSibling elem.insertBefore(doc.createTextNode("b"), splitter) elem.insertBefore(doc.createCDATASection("c"), text1) return doc, elem, text1, splitter, text2 doc, elem, text1, splitter, text2 = setup() text = text1.replaceWholeText("new content") self.checkWholeText(text, "new content") self.checkWholeText(text2, "d") self.confirm(len(elem.childNodes) == 3) doc, elem, text1, splitter, text2 = setup() text = text2.replaceWholeText("new content") self.checkWholeText(text, "new content") self.checkWholeText(text1, "cab") self.confirm(len(elem.childNodes) == 5) doc, elem, text1, splitter, text2 = setup() text = text1.replaceWholeText("") self.checkWholeText(text2, "d") self.confirm(text is None and len(elem.childNodes) == 2) def testSchemaType(self): doc = parseString( "\n" " \n" " \n" "]>") elem = doc.documentElement # We don't want to rely on any specific loader at this point, so # just make sure we can get to all the names, and that the # DTD-based namespace is right. The names can vary by loader # since each supports a different level of DTD information. t = elem.schemaType self.confirm(t.name is None and t.namespace == xml.dom.EMPTY_NAMESPACE) names = "id notid text enum ref refs ent ents nm nms".split() for name in names: a = elem.getAttributeNode(name) t = a.schemaType self.confirm(hasattr(t, "name") and t.namespace == xml.dom.EMPTY_NAMESPACE) def testSetIdAttribute(self): doc = parseString("") e = doc.documentElement a1 = e.getAttributeNode("a1") a2 = e.getAttributeNode("a2") self.confirm(doc.getElementById("v") is None and not a1.isId and not a2.isId) e.setIdAttribute("a1") self.confirm(e.isSameNode(doc.getElementById("v")) and a1.isId and not a2.isId) e.setIdAttribute("a2") self.confirm(e.isSameNode(doc.getElementById("v")) and e.isSameNode(doc.getElementById("w")) and a1.isId and a2.isId) # replace the a1 node; the new node should *not* be an ID a3 = doc.createAttribute("a1") a3.value = "v" e.setAttributeNode(a3) self.confirm(doc.getElementById("v") is None and e.isSameNode(doc.getElementById("w")) and not a1.isId and a2.isId and not a3.isId) # renaming an attribute should not affect its ID-ness: doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") self.confirm(e.isSameNode(doc.getElementById("w")) and a2.isId) def testSetIdAttributeNS(self): NS1 = "http://xml.python.org/ns1" NS2 = "http://xml.python.org/ns2" doc = parseString("") e = doc.documentElement a1 = e.getAttributeNodeNS(NS1, "a1") a2 = e.getAttributeNodeNS(NS2, "a2") self.confirm(doc.getElementById("v") is None and not a1.isId and not a2.isId) e.setIdAttributeNS(NS1, "a1") self.confirm(e.isSameNode(doc.getElementById("v")) and a1.isId and not a2.isId) e.setIdAttributeNS(NS2, "a2") self.confirm(e.isSameNode(doc.getElementById("v")) and e.isSameNode(doc.getElementById("w")) and a1.isId and a2.isId) # replace the a1 node; the new node should *not* be an ID a3 = doc.createAttributeNS(NS1, "a1") a3.value = "v" e.setAttributeNode(a3) self.confirm(e.isSameNode(doc.getElementById("w"))) self.confirm(not a1.isId) self.confirm(a2.isId) self.confirm(not a3.isId) self.confirm(doc.getElementById("v") is None) # renaming an attribute should not affect its ID-ness: doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") self.confirm(e.isSameNode(doc.getElementById("w")) and a2.isId) def testSetIdAttributeNode(self): NS1 = "http://xml.python.org/ns1" NS2 = "http://xml.python.org/ns2" doc = parseString("") e = doc.documentElement a1 = e.getAttributeNodeNS(NS1, "a1") a2 = e.getAttributeNodeNS(NS2, "a2") self.confirm(doc.getElementById("v") is None and not a1.isId and not a2.isId) e.setIdAttributeNode(a1) self.confirm(e.isSameNode(doc.getElementById("v")) and a1.isId and not a2.isId) e.setIdAttributeNode(a2) self.confirm(e.isSameNode(doc.getElementById("v")) and e.isSameNode(doc.getElementById("w")) and a1.isId and a2.isId) # replace the a1 node; the new node should *not* be an ID a3 = doc.createAttributeNS(NS1, "a1") a3.value = "v" e.setAttributeNode(a3) self.confirm(e.isSameNode(doc.getElementById("w"))) self.confirm(not a1.isId) self.confirm(a2.isId) self.confirm(not a3.isId) self.confirm(doc.getElementById("v") is None) # renaming an attribute should not affect its ID-ness: doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an") self.confirm(e.isSameNode(doc.getElementById("w")) and a2.isId) def testPickledDocument(self): doc = parseString("\n" "\n" " \n" "]> text\n" " ") s = pickle.dumps(doc) doc2 = pickle.loads(s) stack = [(doc, doc2)] while stack: n1, n2 = stack.pop() self.confirm(n1.nodeType == n2.nodeType and len(n1.childNodes) == len(n2.childNodes) and n1.nodeName == n2.nodeName and not n1.isSameNode(n2) and not n2.isSameNode(n1)) if n1.nodeType == Node.DOCUMENT_TYPE_NODE: len(n1.entities) len(n2.entities) len(n1.notations) len(n2.notations) self.confirm(len(n1.entities) == len(n2.entities) and len(n1.notations) == len(n2.notations)) for i in range(len(n1.notations)): # XXX this loop body doesn't seem to be executed? no1 = n1.notations.item(i) no2 = n1.notations.item(i) self.confirm(no1.name == no2.name and no1.publicId == no2.publicId and no1.systemId == no2.systemId) stack.append((no1, no2)) for i in range(len(n1.entities)): e1 = n1.entities.item(i) e2 = n2.entities.item(i) self.confirm(e1.notationName == e2.notationName and e1.publicId == e2.publicId and e1.systemId == e2.systemId) stack.append((e1, e2)) if n1.nodeType != Node.DOCUMENT_NODE: self.confirm(n1.ownerDocument.isSameNode(doc) and n2.ownerDocument.isSameNode(doc2)) for i in range(len(n1.childNodes)): stack.append((n1.childNodes[i], n2.childNodes[i])) def testSerializeCommentNodeWithDoubleHyphen(self): doc = create_doc_without_doctype() doc.appendChild(doc.createComment("foo--bar")) self.assertRaises(ValueError, doc.toxml) def testEmptyXMLNSValue(self): doc = parseString("\n" "\n") doc2 = parseString(doc.toxml()) self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) def test_main(): run_unittest(MinidomTest) if __name__ == "__main__": test_main()