import StringIO, sys from xml.dom import Node # MUST be first from xml.dom import implementation, DOMException from xml.dom import HIERARCHY_REQUEST_ERR, NOT_FOUND_ERR from xml.dom import INDEX_SIZE_ERR, INVALID_CHARACTER_ERR, SYNTAX_ERR from xml.dom.ext.reader.Sax2 import FromXml from xml.dom.ext import PrettyPrint # Internal test function: traverse a DOM tree, then verify that all # the parent pointers are correct. Do NOT take this function as an # example of using the Python DOM interface; it knows about the hidden # details of the DOM implementation in order to check them. def _check_dom_tree(t): "Verify that all the parent pointers in a DOM tree are correct" parent = {} # Dict mapping _nodeData instances to their parent nodes = [] # Cumulative list of all the _nodeDatas encountered Queue = [t] # Queue for breadth-first traversal of tree # Do a breadth-first traversal of the DOM tree t while Queue: node = Queue[0] children = node.childNodes for c in children: # Store this node as the parent of each child parent[c] = node # Add each child to the cumulative list nodes.append(c) # Append each child to the queue Queue.append(c) # Remove the node we've just processed Queue = Queue[1:] # OK, now walk over all the children, checking that .parentNode # is correct. count = 0 for n in nodes: p = n.parentNode if p is None: assert not parent.has_key(n) else: assert p == parent[n] count = count + 1 test_text = """ This is a test

Don't panic

Maybe it will work.

We can handle it

Yes we can

Or maybe not

End of test.
""" doc = FromXml(test_text) _check_dom_tree(doc) print 'Simple document' PrettyPrint(doc, sys.stdout) print # Example from the docstring at the top of xml.dom.core.py doc = implementation.createDocument(None,None,None) html = doc.createElement('html') html.setAttribute('attr', 'value') head = doc.createElement('head') title = doc.createElement('title') text = doc.createTextNode("Title goes here") title.appendChild(text) head.appendChild(title) html.appendChild(head) doc.appendChild (html) _check_dom_tree(doc) print '\nOutput of docstring example' PrettyPrint(doc, sys.stdout) print # Detailed test suite for the DOM from xml.dom import Document print '\nRunning detailed test suite' def check(cond, explanation, expected=0): truth = eval(cond) if not truth: if expected: print "XFAIL:", else: print ' *** Failed:', print explanation, '\n\t', cond doc = implementation.createDocument(None,None,None) check('isinstance(doc, Document.Document)', 'createDocument returns a Document') check('doc.parentNode == None', 'Documents have no parent') # Check that documents can only have one child n1 = doc.createElement('n1') ; n2 = doc.createElement('n2') pi = doc.createProcessingInstruction("Processing", "Instruction") doc.appendChild(pi) doc.appendChild(n1) try: doc.appendChild(n1) # n1 should be removed, and then added again except DOMException: print "XFAIL: 4DOM does not support multiple insertion of same node" try: doc.appendChild(n2) except DOMException,e: assert e.code==HIERARCHY_REQUEST_ERR else: print " *** Failed: Document.insertBefore didn't raise HierarchyRequestException" doc.replaceChild(n2, n1) # Should work try: doc.replaceChild(n1, pi) except DOMException,e: assert e.code==HIERARCHY_REQUEST_ERR else: print " *** Failed: Document.replaceChild didn't raise HierarchyRequestException" doc.replaceChild(n2, pi) # Should also work check('pi.parentNode == None', 'Document.replaceChild: PI should have no parent') try: doc.removeChild(n2) except DOMException: print "XFAIL" check('n2.parentNode == None', 'Document.removeChild: n2 should have no parent') # Check adding and deletion with DocumentFragments fragment = doc.createDocumentFragment() ; fragment.appendChild( n1 ) doc.appendChild( fragment ) check('fragment.parentNode == None', 'Doc.appendChild: fragment has no parent') check('n1.parentNode.nodeType == Node.DOCUMENT_NODE', 'Doc.appendChild: n1 now has document as parent') fragment = doc.createDocumentFragment() ; fragment.appendChild( n1 ) n2 = doc.createElement('n2') ; fragment.appendChild( n2 ) try: doc.appendChild( fragment ) except DOMException,e: assert e.code == HIERARCHY_REQUEST_ERR else: print " *** Failed: Document.fragment.appendChild didn't raise HierarchyRequestException" fragment = doc.createDocumentFragment() ; fragment.appendChild( n1 ) n2 = doc.createElement('n2') ; fragment.appendChild( n2 ) doc.appendChild( pi ) try: doc.replaceChild(fragment, pi) except DOMException: assert e.code==HIERARCHY_REQUEST_ERR else: print " *** Failed: Document.fragment.replaceChild didn't raise HierarchyRequestException" #FIXME - fragment.removeChild(n2) fragment.appendChild(pi) doc.appendChild( fragment) check('n1.parentNode == doc', "Document.fragment.replaceChild parent node is correct") _check_dom_tree(doc) # Check adding and deleting children for ordinary nodes n1 = doc.createElement('n1') ; n2 = doc.createElement('n2') check( 'n1.parentNode == None', 'newly created Element has no parent') e1 = doc.createTextNode('e1') ; e2 = doc.createTextNode('e2') e3 = doc.createTextNode('e3') n1.appendChild( e1 ) ; n1.appendChild( e2 ) ; n2.appendChild(e3) # Test .insertBefore with refChild set to a node n2.insertBefore(e1, e3) check('len(n1.childNodes) == 1', "insertBefore: node1 has 1 child") check('len(n2.childNodes) == 2', "insertBefore: node2 has 2 children") check('n1.firstChild.data=="e2"', "insertBefore: node1's child is e2") check('n2.firstChild.data=="e1"', "insertBefore: node2's first child is e1") check('n2.lastChild.data=="e3"', "insertBefore: node2's last child is e3") check('e1.parentNode.tagName == "n2"', "insertBefore: e1's parent is n2") check('e2.parentNode.tagName == "n1"', "insertBefore: e2's parent is n1") check('e3.parentNode.tagName == "n2"', "insertBefore: e3's parent is n3") try: n2.insertBefore(e1, e2) except DOMException,e: assert e.code==NOT_FOUND_ERR else: print " *** Failed: insertBefore didn't raise NotFoundException" # Test .insertBefore with refChild==None n2.insertBefore(e1, None) check('len(n2.childNodes) == 2', "None insertBefore: node1 has 2 children") check('n2.firstChild.data=="e3"', "None insertBefore: node2's first child is e3") check('n2.lastChild.data=="e1"', "None insertBefore: node2's last child is e1") # Test replaceChild ret = n1.replaceChild(e1, e2) check('e2.parentNode == None', "replaceChild: e2 has no parent") check('len(n1.childNodes) == 1', "replaceChild: node1 has 1 child") check('n1.firstChild.data=="e1"', "replaceChild: node1's only child is e1") check('ret.data == "e2"', "replaceChild: returned value node1's only child is e1") try: n1.replaceChild(e2, e2) except DOMException,e: assert e.code==NOT_FOUND_ERR else: print " *** Failed: insertBefore didn't raise NotFoundException" # Test removeChild ret = n1.removeChild( e1 ) check('e1.parentNode == None', "removeChild: e1 has no parent") check('ret.data == "e1"', "removeChild: e1 is the returned value") try: n1.removeChild(e2) except DOMException,e: assert e.code==NOT_FOUND_ERR else: print " *** Failed: removeChild didn't raise NotFoundException" # XXX two more cases for adding stuff: normal, Document, DocumentFragment # Test the functions in the CharacterData interface text = doc.createTextNode('Hello world') #FIXME - check('text[0:5].value == "Hello"', 'text: slicing a node') try: text.substringData(-5, 5) except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: substringData didn't raise IndexSizeException (negative)" try: text.substringData(200, 5) except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: substringData didn't raise IndexSizeException (larger)" try: text.substringData(5, -5) except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: substringData didn't raise IndexSizeException (negcount)" text.appendData('!') check('text.data == "Hello world!"', 'text: appendData') try: text.insertData(-5, 'string') except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: insertData didn't raise IndexSizeException (negative)" try: text.insertData(200, 'string') except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: insertData didn't raise IndexSizeException (larger)" text.insertData(5, ',') check('text.data == "Hello, world!"', 'text: insertData of ","') try: text.deleteData(-5, 5) except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: deleteData didn't raise IndexSizeException (negative)" try: text.deleteData(200, 5) except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: deleteData didn't raise IndexSizeException (larger)" text.deleteData(0, 5) check('text.data == ", world!"', 'text: deleteData of first 5 chars') try: text.replaceData(-5, 5, 'Top of the') except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: replaceData didn't raise IndexSizeException (negative)" try: text.replaceData(200, 5, 'Top of the') except DOMException,e: assert e.code==INDEX_SIZE_ERR else: print " *** Failed: replaceData didn't raise IndexSizeException (larger)" text.replaceData(0, 1, 'Top of the') check('text.data == "Top of the world!"', 'text: deleteData of first 5 chars') # Test the Element class e = doc.createElement('elem') attr = doc.createAttribute('attr2') attr.value = "v2" #check('e.toxml() == ""', 'Element: empty element') check('e.tagName == "elem"', 'Element: tag name') check('len(e.attributes) == 0', 'Element: empty get_attributes') check('e.getAttribute("dummy") == ""', 'Element: empty getAttribute') check('e.getAttributeNode("dummy") == None', 'Element: empty getAttributeNode') try: e.setAttribute('dummy', attr) except DOMException,x: assert x.code == SYNTAX_ERR # Spec says invalid character for name not value # assert x.code==INVALID_CHARACTER_ERR else: print " *** Failed: setAttribute didn't raise InvalidCharacterException" e.setAttribute('dummy', 'value') #check('e.toxml() == ""', 'Element with 1 attribute') check('e.getAttribute("dummy") == "value"', 'Element: getAttribute w/ value') check('e.getAttributeNode("dummy").value == "value"', 'Element: getAttributeNode w/ value') a2 = e.getAttributeNode( 'dummy' ) check('a2.parentNode == None', 'Attribute: should have no parent') check('a2.value == "value"', 'Attribute: value is correct') e.removeAttribute('dummy') check('len(e.attributes) == 0', 'Element: attribute removed') e.setAttributeNode(attr) check('e.attributes[0].value == "v2"', 'Element: attribute node added') a2 = doc.createAttribute('attr2') a2.value = 'v3' ret = e.setAttributeNode(a2) check('e.attributes[0].value == "v3"', 'Element: attribute node replaced') check('ret.value == "v2"', 'Element: deleted attribute node returned') e.removeAttributeNode(a2) check('len(e.attributes) == 0', 'Element: attribute node removed') # Check handling of namespace prefixes #FIXME (start) #e.setAttribute('xmlns', 'http://defaulturi') #e.setAttribute('xmlns:html', 'http://htmluri') #check('e.ns_prefix[""] == "http://defaulturi"', # 'Default namespace with setAttribute') #check('e.ns_prefix["html"] == "http://htmluri"', # 'Prefixed namespace with setAttribute') #e.removeAttribute('xmlns:html') #check('not e.ns_prefix.has_key("html")', # 'Prefixed namespace with removeAttribute') #e.removeAttribute('xmlns') #check('len(e.ns_prefix) == 0', 'Default namespace with removeAttribute') #default = doc.createAttribute('xmlns') ; default.value = "http://defaulturi" #html = doc.createAttribute('xmlns:html') ; html.value = "http://htmluri" #e.setAttributeNode(default) ; e.setAttributeNode(html) #check('e.ns_prefix[""] == "http://defaulturi"', # 'Default namespace with setAttributeNode') #check('e.ns_prefix["html"] == "http://htmluri"', # 'Prefixed namespace with setAttributeNode') #e.removeAttributeNode(html) #check('not e.ns_prefix.has_key("html")', # 'Prefixed namespace with removeAttribute') #e.removeAttributeNode(default) #FIXME (end) # # Check getElementsByTagName # check('len(e.getElementsByTagName("elem")) == 0', "getElementsByTagName doesn't return element") check('len(e.getElementsByTagName("*")) == 0', "getElementsByTagName doesn't return element") # Check CharacterData interfaces using Text nodes t1 = doc.createTextNode('first') ; e.appendChild( t1 ) t2 = doc.createTextNode('second') ; e.appendChild( t2 ) t3 = doc.createTextNode('third') ; e.appendChild( t3 ) #check('e.toxml() == "firstsecondthird"', # "Element: content of three Text nodes as children") check('len(e.childNodes) == 3', 'Element: three Text nodes as children') e.normalize() check('e.firstChild.data == "firstsecondthird"', "Element: normalized Text nodes") check('len(e.childNodes) == 1', 'Element: should be one normalized Text node') check('t2.parentNode == None', 'Element: normalized t2 should have no parent') check('t3.parentNode == None', 'Element: normalized t3 should have no parent') # Text node t1.splitText(5) check('e.firstChild.data == "first"', "Element: newly split Text nodes") check('len(e.childNodes) == 2', 'Text: should be two split Text nodes') check('e.lastChild.data == "secondthird"', "Element: newly split Text nodes") # Check comparisons; e1 and e2 are different proxies for the same underlying # node n1 = doc.createElement('n1') ; n2 = doc.createElement('n2') n1.appendChild(n2) e1 = n1 ; e2 = n2.parentNode check('e1 is e2', 'Two proxies are different according to "is" operator') check('e1 == e2', 'Two proxies are different according to "==" operator') # Done at last! print 'Test suite completed'