From afefc8acd71c2dba76741609b0741ba919c5c5da Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 11:29:55 +0900 Subject: [PATCH 01/21] bpo-19683: Changes the docstring for the test file --- Lib/test/test_minidom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 1663b1f1143ddc..d99d3b0bff548b 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1,4 +1,4 @@ -# test for xml.dom.minidom +"""Tests for xml.dom.minidom.""" import copy import pickle From 24fd486da52698a8fcee346bcae594f97ad94cd2 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 14:09:04 +0900 Subject: [PATCH 02/21] bpo-19683: Adds tests for toxml method --- Lib/test/test_minidom.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index d99d3b0bff548b..182d706a014351 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1663,5 +1663,17 @@ def test_cdata_parsing(self): dom2 = parseString(dom1.toprettyxml()) self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '') + def test_toxml(self): + # simple test + xml_str = '
text
' + root = parseString(xml_str) + self.assertEqual(root.toxml(), '
text
') + self.assertEqual( + root.toxml(standalone=True), + '
text
') + self.assertEqual( + root.toxml(standalone=False), + '
text
') + if __name__ == "__main__": unittest.main() From 172eacd959aac38e11249e8ff6c2253261c82984 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 21:27:01 +0900 Subject: [PATCH 03/21] bpo-19683: Changes the _repr_ for Node Element id in the _repr_ method doesn't seem that useful when sending back a NodeList Adds also docstrings to some methods. --- Lib/xml/dom/minidom.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index d09ef5e7d0371a..bd1966333c2cd4 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -32,6 +32,7 @@ class Node(xml.dom.Node): + """Define properties accessible on a DOM node.""" namespaceURI = None # this is non-null only for elements and attributes parentNode = None ownerDocument = None @@ -44,6 +45,7 @@ def __bool__(self): return True def toxml(self, encoding=None, standalone=None): + """Generate a string representation of a DOM.""" return self.toprettyxml("", "", encoding, standalone) def toprettyxml(self, indent="\t", newl="\n", encoding=None, @@ -66,6 +68,7 @@ def toprettyxml(self, indent="\t", newl="\n", encoding=None, return writer.detach().getvalue() def hasChildNodes(self): + """Return True if the node has children, False else.""" return bool(self.childNodes) def _get_childNodes(self): @@ -864,7 +867,7 @@ def getElementsByTagNameNS(self, namespaceURI, localName): self, namespaceURI, localName, NodeList()) def __repr__(self): - return "" % (self.tagName, id(self)) + return f"" def writexml(self, writer, indent="", addindent="", newl=""): """Write an XML element to a file-like object From 0d60669504b9ef3ecb914cfadfb8ce954ceaf9b7 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 21:39:11 +0900 Subject: [PATCH 04/21] bpo-19683: Reorganize import for clarity --- Lib/test/test_minidom.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 182d706a014351..c89ef4c3d008b3 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1,16 +1,17 @@ """Tests for xml.dom.minidom.""" import copy -import pickle import io +import pickle from test import support import unittest import xml.dom.minidom - -from xml.dom.minidom import parse, Node, Document, parseString +from xml.dom.minidom import Document from xml.dom.minidom import getDOMImplementation - +from xml.dom.minidom import Node +from xml.dom.minidom import parse +from xml.dom.minidom import parseString tstfile = support.findfile("test.xml", subdir="xmltestdata") sample = ("\n" From c106ce5e50252ae6ab76adc32da434b7483e4f4f Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 21:40:12 +0900 Subject: [PATCH 05/21] bpo-19683: Adds encoding test for toxml --- Lib/test/test_minidom.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index c89ef4c3d008b3..d5603423465920 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1675,6 +1675,9 @@ def test_toxml(self): self.assertEqual( root.toxml(standalone=False), '
text
') + self.assertEqual( + root.toxml(encoding="utf-8"), + b'
text
') if __name__ == "__main__": unittest.main() From 847c6f28d9e7bf8cb61b4eb1c1e6ab87d833b126 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 21:41:35 +0900 Subject: [PATCH 06/21] bpo-19683: Reformats hasChildNodes test --- Lib/test/test_minidom.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index d5603423465920..f57d51014fe265 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -626,14 +626,6 @@ def testChildNodes(self): pass def testFirstChild(self): pass - def testHasChildNodes(self): - dom = parseString("") - doc = dom.documentElement - self.assertTrue(doc.hasChildNodes()) - dom2 = parseString("") - doc2 = dom2.documentElement - self.assertFalse(doc2.hasChildNodes()) - def _testCloneElementCopiesAttributes(self, e1, e2, test): attrs1 = e1.attributes attrs2 = e2.attributes @@ -1679,5 +1671,13 @@ def test_toxml(self): root.toxml(encoding="utf-8"), b'
text
') + def test_hasChildNodes(self): + """Test if a node has children.""" + dom = parseString("") + doc = dom.documentElement + self.assertTrue(doc.hasChildNodes()) + dom2 = parseString("") + doc2 = dom2.documentElement + self.assertFalse(doc2.hasChildNodes()) if __name__ == "__main__": unittest.main() From c4889477c6b607d40925a8337dd1a089bf3d4674 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 21:42:35 +0900 Subject: [PATCH 07/21] bpo-19683: Adds test for childNodes --- Lib/test/test_minidom.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index f57d51014fe265..ceebe6999cd9e3 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -6,8 +6,10 @@ from test import support import unittest +from xml.dom.minicompat import NodeList import xml.dom.minidom from xml.dom.minidom import Document +from xml.dom.minidom import Element from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import Node from xml.dom.minidom import parse @@ -622,8 +624,6 @@ def testParseAttributeNamespaces(self): pass def testParseProcessingInstructions(self): pass - def testChildNodes(self): pass - def testFirstChild(self): pass def _testCloneElementCopiesAttributes(self, e1, e2, test): @@ -1679,5 +1679,14 @@ def test_hasChildNodes(self): dom2 = parseString("") doc2 = dom2.documentElement self.assertFalse(doc2.hasChildNodes()) + + def test_childNodes(self): + """Test the list of children Nodes of a DOM Element.""" + dom = parseString('

') + doc = dom.documentElement + self.assertEqual(len(doc.childNodes), 2) + self.assertIsInstance(doc.childNodes[0], Element) + self.assertIsInstance(doc.childNodes, NodeList) + if __name__ == "__main__": unittest.main() From dc3f100fb3aeddbdb61773cc5edcbbe5041ee271 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Thu, 7 Jan 2021 22:42:54 +0900 Subject: [PATCH 08/21] bpo-19683: Adds firstChild and lastChild tests --- Lib/test/test_minidom.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index ceebe6999cd9e3..60d0d451050da6 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -624,8 +624,6 @@ def testParseAttributeNamespaces(self): pass def testParseProcessingInstructions(self): pass - def testFirstChild(self): pass - def _testCloneElementCopiesAttributes(self, e1, e2, test): attrs1 = e1.attributes attrs2 = e2.attributes @@ -1688,5 +1686,19 @@ def test_childNodes(self): self.assertIsInstance(doc.childNodes[0], Element) self.assertIsInstance(doc.childNodes, NodeList) + def test_firstChild(self): + """Test access to the first child of a DOM Element.""" + dom = parseString('

') + doc = dom.documentElement + self.assertEqual(doc.firstChild.toxml(), '') + self.assertIsInstance(doc.firstChild, Element) + + def test_lastChild(self): + """Test access to the last child of a DOM Element.""" + dom = parseString('

') + doc = dom.documentElement + self.assertEqual(doc.lastChild.toxml(), '
') + self.assertIsInstance(doc.lastChild, Element) + if __name__ == "__main__": unittest.main() From 14ad87c4e9ab975edee92b90cda05872a7978516 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Mon, 11 Jan 2021 16:50:33 +0900 Subject: [PATCH 09/21] bpo-19683: Adds test for insertBefore DocumentFragment --- Lib/test/test_minidom.py | 62 ++++++++++++---------------------------- Lib/xml/dom/minidom.py | 9 +++++- 2 files changed, 27 insertions(+), 44 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 60d0d451050da6..78242883240f66 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -9,6 +9,7 @@ from xml.dom.minicompat import NodeList import xml.dom.minidom from xml.dom.minidom import Document +from xml.dom.minidom import DocumentFragment from xml.dom.minidom import Element from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import Node @@ -84,49 +85,6 @@ def testGetElementsByTagName(self): 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") @@ -1700,5 +1658,23 @@ def test_lastChild(self): self.assertEqual(doc.lastChild.toxml(), '
') self.assertIsInstance(doc.lastChild, Element) + def test_insertBefore_with_document_fragment_node(self): + """Test insertBefore for DOCUMENT_FRAGMENT_NODE.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + newNode = dom.createElement('new') + fragment = DocumentFragment() + fragment.appendChild(newNode) + # insertBefore with document fragment + parentNode.insertBefore(fragment, existingNode) + self.assertEqual(parentNode.toxml(), + '') + self.assertIs(parentNode.firstChild, newNode) + self.assertEqual(parentNode.childNodes.length, 2) + # after the insert the fragment is empty + self.assertEqual(fragment.childNodes.length, 0) + if __name__ == "__main__": unittest.main() diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index bd1966333c2cd4..0da558021bae0b 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -83,10 +83,17 @@ def _get_lastChild(self): return self.childNodes[-1] def insertBefore(self, newChild, refChild): + """Insert a new DOM Node before an existing Node. + + https://dom.spec.whatwg.org/#dom-node-insertbefore + The insertBefore(node, child) method, when invoked, + must return the result of pre-inserting node into + this before child. + See also https://dom.spec.whatwg.org/#concept-node-pre-insert + """ if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE: for c in tuple(newChild.childNodes): self.insertBefore(c, refChild) - ### The DOM does not clearly specify what to return in this case return newChild if newChild.nodeType not in self._child_node_types: raise xml.dom.HierarchyRequestErr( From a009350f73dcef4e0df3b213457c97b9351dbe7a Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Fri, 15 Jan 2021 15:35:02 +0900 Subject: [PATCH 10/21] bpo-19683: Adds test for insertBefore for invalid NodeType --- Lib/test/test_minidom.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 78242883240f66..fdeafedb3fb485 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1676,5 +1676,16 @@ def test_insertBefore_with_document_fragment_node(self): # after the insert the fragment is empty self.assertEqual(fragment.childNodes.length, 0) + def test_insertBefore_with_invalid_node_type(self): + """Test insertBefore with invalid node type.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + doc = getDOMImplementation().createDocument(None, "doc", None) + # parentNode.insertBefore(doc, existingNode) will raise + self.assertRaises(xml.dom.HierarchyRequestErr, + parentNode.insertBefore, doc, existingNode) + if __name__ == "__main__": unittest.main() From 709a6404bd1c0416b27dd30cc1b9371277cd1693 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Fri, 15 Jan 2021 15:35:39 +0900 Subject: [PATCH 11/21] bpo-19683: Adds test for insertBefore for text node --- Lib/test/test_minidom.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index fdeafedb3fb485..2d06d05fe744e7 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1687,5 +1687,17 @@ def test_insertBefore_with_invalid_node_type(self): self.assertRaises(xml.dom.HierarchyRequestErr, parentNode.insertBefore, doc, existingNode) + def test_insertBefore_with_text_node(self): + """Test insertBefore for text node.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + newNode = dom.createTextNode('new text') + # insertBefore with text node + parentNode.insertBefore(newNode, existingNode) + self.assertEqual(parentNode.toxml(), + 'new text') + if __name__ == "__main__": unittest.main() From b6fe778a431f0490b5bfd28b87133fa8c5d579d0 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Fri, 15 Jan 2021 15:46:40 +0900 Subject: [PATCH 12/21] bpo-19683: Adds test for insertBefore without ref node --- Lib/test/test_minidom.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 2d06d05fe744e7..14635387a0ea3f 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1699,5 +1699,16 @@ def test_insertBefore_with_text_node(self): self.assertEqual(parentNode.toxml(), 'new text') + def test_insertBefore_with_no_reference_node(self): + """Test insertBefore with no reference node.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + newNode = dom.createTextNode('text trailing') + # insertBefore with no reference node aka None + parentNode.insertBefore(newNode, None) + self.assertEqual(parentNode.toxml(), + 'text trailing') + if __name__ == "__main__": unittest.main() From 95aafaf0bbec13dd63aea7dc35bf30962b9bbb9b Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Tue, 19 Jan 2021 16:07:32 +0900 Subject: [PATCH 13/21] bpo-19683: Adds tests for insertBefore it also removes the old tests so we get more regular tests --- Lib/test/test_minidom.py | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 14635387a0ea3f..7f10b36257eebe 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -98,23 +98,6 @@ def _create_fragment_test_nodes(self): 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("Hello")) @@ -1710,5 +1693,25 @@ def test_insertBefore_with_no_reference_node(self): self.assertEqual(parentNode.toxml(), 'text trailing') + def test_insertBefore_with_no_children(self): + """Test insertBefore with no children node.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + newNode = dom.createElement('new') + existingNode = dom.createElement('existing') + # insertBefore with existing node not existing + self.assertRaises(xml.dom.NotFoundErr, + parentNode.insertBefore, newNode, existingNode) + + def test_insertBefore_return_value(self): + """Test insertBefore returned value.""" + dom = parseString('') + parentNode = dom.documentElement + newNode = dom.createElement('new') + existingNode = parentNode.firstChild + return_value = parentNode.insertBefore(newNode, existingNode) + self.assertEqual(return_value, newNode) + if __name__ == "__main__": unittest.main() From 896543e07cf1899dd1a72e844f262eafc7a5aabe Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Tue, 19 Jan 2021 16:09:23 +0900 Subject: [PATCH 14/21] bpo-19683: Adds tests for appendChild Removes the old tests. Adds new tests in a regular form and testing more cases Adds docstring --- Lib/test/test_minidom.py | 60 +++++++++++++++++++++++++++++----------- Lib/xml/dom/minidom.py | 2 +- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 7f10b36257eebe..83152bd6d8f8c6 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -98,22 +98,6 @@ def _create_fragment_test_nodes(self): frag.appendChild(c3) return dom, orig, c1, c2, c3, frag - def testAppendChild(self): - dom = parse(tstfile) - dom.documentElement.appendChild(dom.createComment("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) @@ -1713,5 +1697,49 @@ def test_insertBefore_return_value(self): return_value = parentNode.insertBefore(newNode, existingNode) self.assertEqual(return_value, newNode) + def test_appendChild(self): + """Test appendChild for a simple node.""" + dom = parseString('') + parentNode = dom.documentElement + newNode = dom.createElement('new') + # append a new node child + parentNode.appendChild(newNode) + self.assertEqual(parentNode.toxml(), + '') + + def test_appendChild_with_document_fragment_node(self): + """Test appendChild for DOCUMENT_FRAGMENT_NODE.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + newNode = dom.createElement('new') + fragment = DocumentFragment() + fragment.appendChild(newNode) + # appendChild with document fragment + parentNode.appendChild(fragment) + self.assertEqual(parentNode.toxml(), + '') + # after the appendChild, the fragment should be empty + self.assertEqual(fragment.childNodes.length, 0) + + def test_appendChild_with_invalid_node_type(self): + """Test appendChild with invalid node type.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + doc = getDOMImplementation().createDocument(None, "doc", None) + # parentNode.appendChild(doc) will raise + self.assertRaises(xml.dom.HierarchyRequestErr, + parentNode.appendChild, doc) + + def test_appendChild_return_value(self): + """Test appendChild returned value.""" + dom = parseString('') + parentNode = dom.documentElement + newNode = dom.createElement('new') + # append a new node child + return_value = parentNode.appendChild(newNode) + self.assertEqual(return_value, newNode) + if __name__ == "__main__": unittest.main() diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 0da558021bae0b..77bfe63b5d865c 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -122,10 +122,10 @@ def insertBefore(self, newChild, refChild): return newChild def appendChild(self, node): + """Append a child node to an existing node.""" if node.nodeType == self.DOCUMENT_FRAGMENT_NODE: for c in tuple(node.childNodes): self.appendChild(c) - ### The DOM does not clearly specify what to return in this case return node if node.nodeType not in self._child_node_types: raise xml.dom.HierarchyRequestErr( From 4dc05cab6de0fed4512286debd6d37b5eedac5db Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Tue, 19 Jan 2021 16:14:03 +0900 Subject: [PATCH 15/21] bpo-19683: Removes tests which are empty These tests are useless for now. We will be adding them again later with proper code. --- Lib/test/test_minidom.py | 61 ---------------------------------------- 1 file changed, 61 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 83152bd6d8f8c6..eb0903fd750245 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -308,15 +308,6 @@ def testChangeAttr(self): and el.getAttribute("spam2") == "bam2") dom.unlink() - def testGetAttrList(self): - pass - - def testGetAttrValues(self): - pass - - def testGetAttrLength(self): - pass - def testGetAttribute(self): dom = Document() child = dom.appendChild( @@ -337,8 +328,6 @@ def testGetAttributeNS(self): self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"), '') - def testGetAttributeNode(self): pass - def testGetElementsByTagNameNS(self): d=""" @@ -409,8 +398,6 @@ def testAttributeRepr(self): self.confirm(str(node) == repr(node)) dom.unlink() - def testTextNodeRepr(self): pass - def testWriteXML(self): str = '' dom = parseString(str) @@ -474,14 +461,6 @@ def testProcessingInstruction(self): and pi.localName is None and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE) - def testProcessingInstructionRepr(self): pass - - def testTextRepr(self): pass - - def testWriteText(self): pass - - def testDocumentElement(self): pass - def testTooManyDocumentElements(self): doc = parseString("") elem = doc.createElement("extra") @@ -490,26 +469,6 @@ def testTooManyDocumentElements(self): elem.unlink() doc.unlink() - def testCreateElementNS(self): pass - - def testCreateAttributeNS(self): pass - - def testParse(self): pass - - def testParseString(self): pass - - def testComment(self): pass - - def testAttrListItem(self): pass - - def testAttrListItems(self): pass - - def testAttrListItemNS(self): pass - - def testAttrListKeys(self): pass - - def testAttrListKeysNS(self): pass - def testRemoveNamedItem(self): doc = parseString("") e = doc.documentElement @@ -529,26 +488,6 @@ def testRemoveNamedItemNS(self): self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS, "http://xml.python.org/", "b") - def testAttrListValues(self): pass - - def testAttrListLength(self): pass - - def testAttrList__getitem__(self): pass - - def testAttrList__setitem__(self): pass - - def testSetAttrValueandNodeValue(self): pass - - def testParseElement(self): pass - - def testParseAttributes(self): pass - - def testParseElementNamespaces(self): pass - - def testParseAttributeNamespaces(self): pass - - def testParseProcessingInstructions(self): pass - def _testCloneElementCopiesAttributes(self, e1, e2, test): attrs1 = e1.attributes attrs2 = e2.attributes From ff99b90a812c002cd7581db4564839bb3350e4e1 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Tue, 19 Jan 2021 16:15:23 +0900 Subject: [PATCH 16/21] bpo-19683: Adds tests firstChild, lastChild Some parts of the function were not tested. This is fixing it. --- Lib/test/test_minidom.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index eb0903fd750245..f727af01c41f25 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1556,6 +1556,9 @@ def test_firstChild(self): doc = dom.documentElement self.assertEqual(doc.firstChild.toxml(), '') self.assertIsInstance(doc.firstChild, Element) + dom = parseString('
') + doc = dom.documentElement + self.assertIsNone(doc.firstChild) def test_lastChild(self): """Test access to the last child of a DOM Element.""" @@ -1563,6 +1566,9 @@ def test_lastChild(self): doc = dom.documentElement self.assertEqual(doc.lastChild.toxml(), '
') self.assertIsInstance(doc.lastChild, Element) + dom = parseString('
') + doc = dom.documentElement + self.assertIsNone(doc.lastChild) def test_insertBefore_with_document_fragment_node(self): """Test insertBefore for DOCUMENT_FRAGMENT_NODE.""" From e5654e83bcd906310679fbe02b9492a503cd3548 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Tue, 19 Jan 2021 22:16:36 +0900 Subject: [PATCH 17/21] bpo-19683: Adds tests for replaceChild to xml.dom.test_minidom * Convert old tests to have a regular form * Adds new tests to cover more cases. --- Lib/test/test_minidom.py | 97 ++++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 18 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index f727af01c41f25..3f8ceb7c9dab48 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -98,15 +98,6 @@ def _create_fragment_test_nodes(self): frag.appendChild(c3) return dom, orig, c1, c2, c3, frag - 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') @@ -1228,15 +1219,6 @@ def testWholeText(self): 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") @@ -1686,5 +1668,84 @@ def test_appendChild_return_value(self): return_value = parentNode.appendChild(newNode) self.assertEqual(return_value, newNode) + def test_replaceChild(self): + """Test replaceChild for a simple node.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + newNode = dom.createElement('new') + # replace existingNode by newNode + parentNode.replaceChild(newNode, existingNode) + self.assertEqual(parentNode.toxml(), '') + + def test_replaceChild_with_document_fragment_node(self): + """Test replaceChild for DOCUMENT_FRAGMENT_NODE.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + newNode = dom.createElement('new') + fragment = DocumentFragment() + fragment.appendChild(newNode) + # replaceChild with document fragment + parentNode.replaceChild(fragment, existingNode) + self.assertEqual(parentNode.toxml(), '') + # after the replaceChild, the fragment should be empty + self.assertEqual(fragment.childNodes.length, 0) + + def test_replaceChild_with_invalid_node_type(self): + """Test replaceChild with invalid node type.""" + # Preparing the test + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + doc = getDOMImplementation().createDocument(None, "doc", None) + # parentNode.replaceChild(doc, existingNode) will raise + self.assertRaises(xml.dom.HierarchyRequestErr, + parentNode.replaceChild, doc, existingNode) + + def test_replaceChild_with_same_node(self): + """Test replaceChild with same node.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + # replace existingNode by existingNode + parentNode.replaceChild(existingNode, existingNode) + self.assertEqual(parentNode.toxml(), '') + + def test_replaceChild_with_new_child_parent_not_None(self): + """Test replaceChild with new child parent is not Nonce.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + newdom = parseString('') + newparentNode = newdom.documentElement + newNode = newparentNode.firstChild + # replace existingNode by newNode + parentNode.replaceChild(newNode, existingNode) + self.assertEqual(parentNode.toxml(), '') + + def test_replaceChild_with_no_existing_node(self): + """Test replaceChild with missing existing node.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = dom.createElement('existing') + newdom = parseString('') + newparentNode = newdom.documentElement + newNode = newparentNode.firstChild + # replace existingNode by newNode + self.assertRaises(xml.dom.NotFoundErr, + parentNode.replaceChild, newNode, existingNode) + + def test_replaceChild_return_value(self): + """Test replaceChild returned value.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + newNode = dom.createElement('new') + # replace a new node child + return_value = parentNode.replaceChild(newNode, existingNode) + self.assertEqual(return_value, existingNode) + if __name__ == "__main__": unittest.main() From 0da118b0dfae173bb6a9ee4c267adb22cc5eabf8 Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Tue, 19 Jan 2021 22:44:00 +0900 Subject: [PATCH 18/21] bpo-19683: Adds tests for removeChild * Adds tests for removeChild * Remove old tests * Fix the docstring for removeChild and replaceChild --- Lib/test/test_minidom.py | 27 ++++++++++++++++++--------- Lib/xml/dom/minidom.py | 3 ++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 3f8ceb7c9dab48..1745ad7b745409 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1449,15 +1449,6 @@ def testExceptionOnSpacesInXMLNSValue(self): with self.assertRaisesRegex(ValueError, 'Unsupported syntax'): parseString('') - def testDocRemoveChild(self): - doc = parse(tstfile) - title_tag = doc.documentElement.getElementsByTagName("TITLE")[0] - self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag) - num_children_before = len(doc.childNodes) - doc.removeChild(doc.childNodes[0]) - num_children_after = len(doc.childNodes) - self.assertTrue(num_children_after == num_children_before - 1) - def testProcessingInstructionNameError(self): # wrong variable in .nodeValue property will # lead to "NameError: name 'data' is not defined" @@ -1747,5 +1738,23 @@ def test_replaceChild_return_value(self): return_value = parentNode.replaceChild(newNode, existingNode) self.assertEqual(return_value, existingNode) + def test_removeChild(self): + """Test removeChild for a simple node.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = parentNode.firstChild + # remove an existing node child + parentNode.removeChild(existingNode) + self.assertEqual(parentNode.toxml(), '') + + def test_removeChild_with_no_existing_node(self): + """Test removeChild with missing existing node.""" + dom = parseString('') + parentNode = dom.documentElement + existingNode = dom.createElement('existing') + # remove non existing Node + self.assertRaises(xml.dom.NotFoundErr, + parentNode.removeChild, existingNode) + if __name__ == "__main__": unittest.main() diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 77bfe63b5d865c..8c69878544de4f 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -139,6 +139,7 @@ def appendChild(self, node): return node def replaceChild(self, newChild, oldChild): + """Replace an existing node with a new node.""" if newChild.nodeType == self.DOCUMENT_FRAGMENT_NODE: refChild = oldChild.nextSibling self.removeChild(oldChild) @@ -171,6 +172,7 @@ def replaceChild(self, newChild, oldChild): return oldChild def removeChild(self, oldChild): + """Remove an existing child.""" try: self.childNodes.remove(oldChild) except ValueError: @@ -182,7 +184,6 @@ def removeChild(self, oldChild): oldChild.nextSibling = oldChild.previousSibling = None if oldChild.nodeType in _nodeTypes_with_children: _clear_id_cache(self) - oldChild.parentNode = None return oldChild From d0d51f233de55db3ccf66e96ba96e927bca5cd8a Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Fri, 19 Feb 2021 14:38:53 +0900 Subject: [PATCH 19/21] bpo-19683: Adds tests for normalize. Part 1 --- Lib/test/test_minidom.py | 290 ++++++++++++--------------------------- 1 file changed, 88 insertions(+), 202 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 1745ad7b745409..d4d6f717bb6667 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -734,208 +734,6 @@ def testCloneNodeEntity(self): self.check_clone_node_entity(False) self.check_clone_node_entity(True) - 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 @@ -1756,5 +1554,93 @@ def test_removeChild_with_no_existing_node(self): self.assertRaises(xml.dom.NotFoundErr, parentNode.removeChild, existingNode) + def test_normalize_text_nodes(self): + """Test normalize two text nodes.""" + doc = parseString("") + root = doc.documentElement + root.appendChild(doc.createTextNode("first")) + root.appendChild(doc.createTextNode("second")) + # Two text nodes have been created + # len(root.childNodes) == 2 + # root.firstChild.data == 'first' + doc.normalize() + self.assertEqual(len(root.childNodes), 1) + self.assertEqual(root.firstChild.data, 'firstsecond') + self.assertEqual(root.toxml(), 'firstsecond') + + + def test_normalize_empty_text_node(self): + """Test normalize removing the childNode of an empty text node.""" + doc = parseString("") + root = doc.documentElement + root.appendChild(doc.createTextNode("")) + # len(root.childNodes) == 1 + # root.firstChild.data == '' + doc.normalize() + self.assertEqual(len(root.childNodes), 0) + self.assertIs(root.firstChild, None) + self.assertEqual(root.toxml(), '') + + + def test_normalize_text_node_and_element_node(self): + """Test normalize merges two text nodes but not element node.""" + doc = parseString("") + root = doc.documentElement + root.appendChild(doc.createTextNode("first")) + root.appendChild(doc.createTextNode("second")) + root.appendChild(doc.createElement("element")) + # len(root.childNodes) == 3 + # root.firstChild.data == 'first' + doc.normalize() + self.assertEqual(len(root.childNodes), 2) + self.assertEqual(root.firstChild.data, 'firstsecond') + self.assertEqual(root.toxml(), 'firstsecond') + + def test_normalize_two_text_nodes_with_first_empty(self): + """Test that normalize removes the first empty text node only.""" + doc = parseString("") + root = doc.documentElement + root.appendChild(doc.createTextNode("")) + root.appendChild(doc.createTextNode("second")) + # Two text nodes have been created + # len(root.childNodes) == 2 + # root.firstChild.data == '' + # root.lastChild.data == 'second' + doc.normalize() + self.assertEqual(len(root.childNodes), 1) + self.assertEqual(root.firstChild.data, 'second') + self.assertEqual(root.toxml(), 'second') + + def test_normalize_two_text_nodes_with_last_empty(self): + """Test that normalize removes the last empty text node only.""" + doc = parseString("") + root = doc.documentElement + root.appendChild(doc.createTextNode("first")) + root.appendChild(doc.createTextNode("")) + # Two text nodes have been created + # len(root.childNodes) == 2 + # root.firstChild.data == 'first' + # root.lastChild.data == '' + doc.normalize() + self.assertEqual(len(root.childNodes), 1) + self.assertEqual(root.firstChild.data, 'first') + self.assertEqual(root.toxml(), 'first') + + def test_normalize_two_element_nodes_with_empty_text_node(self): + """Test that normalize removes the empty text node in between.""" + doc = parseString("") + root = doc.documentElement + root.appendChild(doc.createElement('first')) + root.appendChild(doc.createTextNode('')) + root.appendChild(doc.createElement('second')) + # Two text nodes have been created + # len(root.childNodes) == 3 + # root.toxml() == '' + doc.normalize() + self.assertEqual(len(root.childNodes), 2) + self.assertEqual(root.toxml(), '') + +# HERE + if __name__ == "__main__": unittest.main() From 52c26c53c64523fcc29b7ef56ac44f47a64a7f3b Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Fri, 19 Feb 2021 15:33:38 +0900 Subject: [PATCH 20/21] bpo-19683: Adds tests for normalize. Part 2 --- Lib/test/test_minidom.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index d4d6f717bb6667..4cc2d0163a835b 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -1640,7 +1640,43 @@ def test_normalize_two_element_nodes_with_empty_text_node(self): self.assertEqual(len(root.childNodes), 2) self.assertEqual(root.toxml(), '') -# HERE + def text_normalize_nested_elements_with_text_nodes(self): + """Test that normalize is still working in a more complex tree.""" + xml = 'texttext' + doc = parseString(xml) + root = doc.documentElement + root.childNodes[0].appendChild(doc.createTextNode('added')) + root.childNodes[1].appendChild(doc.createTextNode('')) + doc.normalize() + self.assertEqual(len(root.childNodes[0]), 1) + self.assertEqual(len(root.childNodes[1]), 1) + self.assertEqual( + root.toxml(), + 'textaddedtext') + + def test_normalize_on_text_node(self): + """Test that normalize on a text Node doesn't fail. + + See https://bugs.python.org/issue777884 + """ + doc = parseString("text") + text = doc.documentElement.childNodes[0] + self.assertEqual(text.nodeType, Node.TEXT_NODE) + # Should run quietly, doing nothing. + text.normalize() + + def test_normalize_unlink_child_node(self): + """Test that normalize unlinks empty child nodes. + + See https://bugs.python.org/issue1433694 + """ + doc = parseString("text") + root = doc.documentElement + root.childNodes[1].nodeValue = '' + doc.normalize() + # Final child's .nextSibling should be None + self.assertIsNone(root.childNodes[-1].nextSibling) + if __name__ == "__main__": unittest.main() From c8ef4604ec5d0249d081e123e5c176cedae7f23b Mon Sep 17 00:00:00 2001 From: Karl Dubost Date: Fri, 19 Feb 2021 16:33:31 +0900 Subject: [PATCH 21/21] bpo-19683: Adds cloneNode tests. Part 1 --- Lib/test/test_minidom.py | 97 +++++++++++++++++++--------------------- Lib/xml/dom/minidom.py | 12 ++++- 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py index 4cc2d0163a835b..99c94ff14ca36d 100644 --- a/Lib/test/test_minidom.py +++ b/Lib/test/test_minidom.py @@ -479,56 +479,6 @@ def testRemoveNamedItemNS(self): 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 = list(attrs1.keys()) - keys2 = list(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" "" @@ -1677,6 +1627,53 @@ def test_normalize_unlink_child_node(self): # Final child's .nextSibling should be None self.assertIsNone(root.childNodes[-1].nextSibling) + def test_cloneNode_with_simple_element(self): + """Test cloneNode for a simple Element Node.""" + doc = parseString("") + root = doc.documentElement + new_clone = root.cloneNode(deep=True) + self.assertEqual(new_clone.toxml(), root.toxml()) + self.assertEqual(new_clone.nodeType, root.nodeType) + new_clone = root.cloneNode(deep=False) + self.assertEqual(new_clone.toxml(), '') + + def test_cloneNode_with_text_node(self): + """Test cloneNode with text node.""" + doc = parseString("text") + root = doc.documentElement + new_clone = root.cloneNode(deep=True) + self.assertEqual(new_clone.toxml(), root.toxml()) + self.assertEqual(new_clone.nodeType, root.nodeType) + new_clone = root.cloneNode(deep=False) + self.assertEqual(new_clone.toxml(), '') + + def test_cloneNode_with_text_and_element_nodes(self): + """Test cloneNode with text and element nodes.""" + doc = parseString("text") + root = doc.documentElement + new_clone = root.cloneNode(deep=True) + self.assertEqual(new_clone.toxml(), root.toxml()) + self.assertEqual(new_clone.nodeType, root.nodeType) + new_clone = root.cloneNode(deep=False) + self.assertEqual(new_clone.toxml(), '') + + def test_cloneNode_with_element_node_with_attribute(self): + """Test cloneNode with element nodes with attribute.""" + doc = parseString("") + root = doc.documentElement + new_clone = root.cloneNode(deep=True) + self.assertEqual(new_clone.toxml(), root.toxml()) + self.assertEqual(new_clone.nodeType, root.nodeType) + new_clone = root.cloneNode(deep=False) + self.assertEqual(new_clone.toxml(), '') + + def test_cloneNode_parentNode_None(self): + """Test cloneNode that the parent node is None.""" + doc = parseString("") + root = doc.documentElement + new_clone = root.firstChild.cloneNode(deep=True) + self.assertEqual(new_clone.toxml(), '') + self.assertIsNone(new_clone.parentNode) if __name__ == "__main__": unittest.main() diff --git a/Lib/xml/dom/minidom.py b/Lib/xml/dom/minidom.py index 8c69878544de4f..916c0503b3d39b 100644 --- a/Lib/xml/dom/minidom.py +++ b/Lib/xml/dom/minidom.py @@ -188,6 +188,12 @@ def removeChild(self, oldChild): return oldChild def normalize(self): + """Transform a node into its normalized form. + + Removes empty exclusive Text nodes and concatenates the data of + remaining contiguous exclusive Text nodes into the first of + their nodes. + """ L = [] for child in self.childNodes: if child.nodeType == Node.TEXT_NODE: @@ -215,6 +221,7 @@ def normalize(self): self.childNodes[:] = L def cloneNode(self, deep): + """The Node.cloneNode() method returns a duplicate of the node.""" return _clone_node(self, deep, self.ownerDocument or self) def isSupported(self, feature, version): @@ -1334,6 +1341,7 @@ def _get_internalSubset(self): return self.internalSubset def cloneNode(self, deep): + """The Node.cloneNode() method returns a duplicate of the node.""" if self.ownerDocument is None: # it's ok clone = DocumentType(None) @@ -1897,7 +1905,9 @@ def renameNode(self, n, namespaceURI, name): def _clone_node(node, deep, newOwnerDocument): """ - Clone a node and give it the new owner document. + Returns a copy of node. + + If deep is true, the copy also includes the node’s descendants. Called by Node.cloneNode and Document.importNode """ if node.ownerDocument.isSameNode(newOwnerDocument):