PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Lib/test/test_minidom.py

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