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