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