Logo Search packages:      
Sourcecode: zope-parsedxml version File versions

CoreLvl1.py

##############################################################################
# 
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
# 
# Copyright (c) Digital Creations.  All rights reserved.
# 
# This license has been certified as Open Source(tm).
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# 
# 1. Redistributions in source code must retain the above copyright
#    notice, this list of conditions, and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions, and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 
# 3. Digital Creations requests that attribution be given to Zope
#    in any manner possible. Zope includes a "Powered by Zope"
#    button that is installed by default. While it is not a license
#    violation to remove this button, it is requested that the
#    attribution remain. A significant investment has been put
#    into Zope, and this effort will continue if the Zope community
#    continues to grow. This is one way to assure that growth.
# 
# 4. All advertising materials and documentation mentioning
#    features derived from or use of this software must display
#    the following acknowledgement:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    In the event that the product being advertised includes an
#    intact Zope distribution (with copyright and license included)
#    then this clause is waived.
# 
# 5. Names associated with Zope or Digital Creations must not be used to
#    endorse or promote products derived from this software without
#    prior written permission from Digital Creations.
# 
# 6. Modified redistributions of any form whatsoever must retain
#    the following acknowledgment:
# 
#      "This product includes software developed by Digital Creations
#      for use in the Z Object Publishing Environment
#      (http://www.zope.org/)."
# 
#    Intact (re-)distributions of any official Zope release do not
#    require an external acknowledgement.
# 
# 7. Modifications are encouraged but must be packaged separately as
#    patches to official Zope releases.  Distributions that do not
#    clearly separate the patches from the original work must be clearly
#    labeled as unofficial distributions.  Modifications which do not
#    carry the name Zope may be packaged in any form, as long as they
#    conform to all of the clauses above.
# 
# 
# Disclaimer
# 
#   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
#   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
#   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
#   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
#   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
#   SUCH DAMAGE.
# 
# 
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations.  Specific
# attributions are listed in the accompanying credits file.
# 
##############################################################################

from Base import *

import string
import xml.dom
from xml.dom import Node


# --- DOMImplementation

class DOMImplementationReadTestCase(TestCaseBase):

    def setUp(self):
        # self.implementation is already set.
        pass

    # Combinations to feed to the hasFeature method; some of these will always
    # return false, of course.
    # ('Core', '1.0') was never defined for DOM level 1, it was implicit. Most
    # DOM level 2 implementations return true, but this is a courtesy.
    featureMatrix = (
        ('Core', None),
        #('Core', '1.0'),
        ('Core', '2.0'),
        ('Core', '3.0'),
        
        ('XML', None),
        ('XML', '1.0'),
        ('XML', '2.0'),
        ('XML', '3.0'),

        ('Traversal', None),
        ('Traversal', '1.0'),
        ('Traversal', '2.0'),
        ('Traversal', '3.0'),

        ('BogusFeature', None),
        ('BogusFeature', '1.0'),
        ('BogusFeature', '2.0'),
        ('BogusFeature', '3.0'),
    )

    def checkHasFeature(self):
        impl = self.implementation
        for feature, level in self.featureMatrix:
            result1 = impl.hasFeature(feature, level)
            result2 = impl.hasFeature(string.upper(feature), level)
            result3 = impl.hasFeature(string.lower(feature), level)

            expect = ((feature, level) in self.supportedFeatures)

            assert result1 == result2 and result1 == result3, (
                "Different results from different case feature string.")

            assert (result1 and 1 or 0) == expect, (
                "Test for %s, version %s should have returned %s, "
                "returned %s" % (repr(feature), repr(level), repr(expect),
                    repr(result1)))


# --- Node

class NodeReadTestCaseBase(TestCaseBase):

    nodeTypes = (
        'ATTRIBUTE_NODE',
        'CDATA_SECTION_NODE',
        'COMMENT_NODE',
        'DOCUMENT_FRAGMENT_NODE',
        'DOCUMENT_NODE',
        'DOCUMENT_TYPE_NODE',
        'ELEMENT_NODE',
        'ENTITY_NODE',
        'ENTITY_REFERENCE_NODE',
        'NOTATION_NODE',
        'PROCESSING_INSTRUCTION_NODE',
        'TEXT_NODE',
    )

    def checkNodeTypeConstants(self):
        for type in self.nodeTypes:
            checkAttribute(self.node, type, getattr(Node, type))
    
    def checkAttributes(self):
        if self.node.nodeType == Node.ELEMENT_NODE:
            checkLength(self.node.attributes, 0)
        else:
            checkAttribute(self.node, 'attributes', None)

        checkReadOnly(self.node, 'attributes')

    def checkChildNodes(self):
        if self.node.nodeType in (Node.ATTRIBUTE_NODE,
                Node.DOCUMENT_NODE,
                Node.ENTITY_NODE):
            expectedLength = 1

        else:
            expectedLength = 0

        checkLength(self.node.childNodes, expectedLength)
        checkReadOnly(self.node, 'childNodes')

    def checkFirstChild(self):
        # The following node types are expected to have one childNode.
        if self.node.nodeType not in (Node.ATTRIBUTE_NODE,
                Node.DOCUMENT_NODE,
                Node.ENTITY_NODE):
            expected = None

        else:
            expected = self.node.childNodes[0]

        checkAttributeSameNode(self.node, 'firstChild', expected)
        checkReadOnly(self.node, 'firstChild')
        if expected:
            checkAttributeSameNode(self.node.firstChild, 'parentNode', 
                self.node)

    def checkLastChild(self):
        # The following node types are expected to have one childNode.
        if self.node.nodeType not in (Node.ATTRIBUTE_NODE,
                Node.DOCUMENT_NODE,
                Node.ENTITY_NODE):
            expected = None

        else:
            expected = self.node.childNodes[0]

        checkAttributeSameNode(self.node, 'lastChild', expected)
        checkReadOnly(self.node, 'lastChild')
        if expected:
            checkAttributeSameNode(self.node.lastChild, 'parentNode', 
                self.node)

    def checkNextSibling(self):
        checkAttribute(self.node, 'nextSibling', None)
        checkReadOnly(self.node, 'nextSibling')

    nodeNameMap = {
        Node.CDATA_SECTION_NODE:     '#cdata-section',
        Node.COMMENT_NODE:           '#comment',
        Node.DOCUMENT_NODE:          '#document',
        Node.DOCUMENT_FRAGMENT_NODE: '#document-fragment',
        Node.TEXT_NODE:              '#text',
    }
            
    def checkNodeName(self):
        if self.node.nodeType in (Node.ATTRIBUTE_NODE,
                Node.DOCUMENT_TYPE_NODE):
            expected = self.node.name

        elif self.node.nodeType == Node.ELEMENT_NODE:
            expected = self.node.tagName

        elif self.node.nodeType in (Node.ENTITY_NODE,
                Node.ENTITY_REFERENCE_NODE,
                Node.NOTATION_NODE):
            expected = self.expectedNodeName

        elif self.node.nodeType == Node.PROCESSING_INSTRUCTION_NODE:
            expected = self.node.target

        else:
            expected = self.nodeNameMap[self.node.nodeType]

        checkAttribute(self.node, 'nodeName', expected)
        checkReadOnly(self.node, "nodeName")
 
    def checkNodeType(self):
        checkAttribute(self.node, 'nodeType', self.expectedType)
        checkReadOnly(self.node, "nodeType")

    emptyNodeValueList = (
        Node.DOCUMENT_FRAGMENT_NODE,
        Node.DOCUMENT_NODE,
        Node.DOCUMENT_TYPE_NODE,
        Node.ELEMENT_NODE,
        Node.ENTITY_NODE,
        Node.ENTITY_REFERENCE_NODE,
        Node.NOTATION_NODE,
    )

    def checkNodeValue(self):
        if self.node.nodeType in self.emptyNodeValueList:
            expected = None

        elif self.node.nodeType in (Node.CDATA_SECTION_NODE,
                Node.COMMENT_NODE,
                Node.TEXT_NODE,
                Node.PROCESSING_INSTRUCTION_NODE):
            expected = self.node.data
        
        elif self.node.nodeType == Node.ATTRIBUTE_NODE:
            expected = self.node.value

        checkAttribute(self.node, 'nodeValue', expected)

    def checkParentNode(self):
        checkAttribute(self.node, 'parentNode', None)
        checkReadOnly(self.node, 'parentNode')

    def checkPreviousSibling(self):
        checkAttribute(self.node, 'previousSibling', None)
        checkReadOnly(self.node, 'previousSibling')

    def hasChildNodes(self):
        if self.node.nodeType in (Node.ATTRIBUTE_NODE,
                Node.DOCUMENT_NODE,
                Node.ENTITY_NODE):
            expectTrue = 1
        else:
            expectTrue = 0

        if expectTrue:
            assert self.node.hasChildNodes(), \
                   "hasChildNodes returned 'false' when 'true' was expected."
        else:
            assert not self.node.hasChildNodes(), \
                   "hasChildNodes returned 'true' when 'false' was expected."


class NodeWriteTestCaseBase(TestCaseBase):

    TEST_NAME = 'somename'

    emptyNodeValueList = NodeReadTestCaseBase.emptyNodeValueList

    readOnlyNodeList = (
        Node.ENTITY_NODE,
        Node.ENTITY_REFERENCE_NODE,
        Node.NOTATION_NODE,
    )

    def checkNodeValue(self):
        # Nodetypes that are read-only.
        if self.node.nodeType in self.readOnlyNodeList:
            checkReadOnly(self.node, 'nodeValue')
            return

        # Nodetypes that should ignore changes.
        if self.node.nodeType in self.emptyNodeValueList:
            self.node.nodeValue = "Ignore this."
            checkAttribute(self.node, "nodeValue", None)
            return

        # Nodetypes where nodeValue is an alias.
        if self.node.nodeType in (Node.CDATA_SECTION_NODE,
                Node.COMMENT_NODE,
                Node.TEXT_NODE,
                Node.PROCESSING_INSTRUCTION_NODE):
            alias = 'data'
        
        elif self.node.nodeType == Node.ATTRIBUTE_NODE:
            alias = 'value'

        self.node.nodeValue = 'foo'
        checkAttribute(self.node, 'nodeValue', 'foo')
        checkAttribute(self.node, alias, 'foo')

    allowedChildrenMap = {
        Node.DOCUMENT_NODE: (
            Node.ELEMENT_NODE, # Maximum of one, special case
            Node.PROCESSING_INSTRUCTION_NODE,
            Node.COMMENT_NODE,
            Node.DOCUMENT_TYPE_NODE, # Maximum of one, special case
        ),

        Node.DOCUMENT_FRAGMENT_NODE: (
            Node.ELEMENT_NODE,
            Node.PROCESSING_INSTRUCTION_NODE,
            Node.COMMENT_NODE,
            Node.TEXT_NODE,
            Node.CDATA_SECTION_NODE,
            Node.ENTITY_REFERENCE_NODE,
        ),

        Node.DOCUMENT_TYPE_NODE: (),

        Node.ENTITY_REFERENCE_NODE: (
            Node.ELEMENT_NODE,
            Node.PROCESSING_INSTRUCTION_NODE,
            Node.COMMENT_NODE,
            Node.TEXT_NODE,
            Node.CDATA_SECTION_NODE,
            Node.ENTITY_REFERENCE_NODE,
        ),

        Node.ELEMENT_NODE: (
            Node.ELEMENT_NODE,
            Node.TEXT_NODE,
            Node.COMMENT_NODE,
            Node.PROCESSING_INSTRUCTION_NODE,
            Node.CDATA_SECTION_NODE,
            Node.ENTITY_REFERENCE_NODE,
        ),

        Node.ATTRIBUTE_NODE: (
           Node.TEXT_NODE,
           Node.ENTITY_REFERENCE_NODE,
        ),

        Node.PROCESSING_INSTRUCTION_NODE: (),

        Node.COMMENT_NODE: (),

        Node.TEXT_NODE: (),

        Node.CDATA_SECTION_NODE: (),

        Node.ENTITY_NODE: (
            Node.ELEMENT_NODE,
            Node.PROCESSING_INSTRUCTION_NODE,
            Node.COMMENT_NODE,
            Node.TEXT_NODE,
            Node.CDATA_SECTION_NODE,
            Node.ENTITY_REFERENCE_NODE,
        ),

        Node.NOTATION_NODE: (),
    }

    nodeCreateMap = {
        # from Document
        'createAttribute':             ('anAttr',),
        'createCDATASection':          ('a CDATA Section',),
        'createComment':               ('a Comment',),
        'createDocumentFragment':      (),
        'createElement':               ('anElement',),
        'createEntityReference':       ('anEntityReference',),
        'createProcessingInstruction': ('aPI', 'data for PI'),
        'createTextNode':              ('A Text Node',),
        
        # From DOMImplementation (marked as tuples)
        ('createDocument',):           (None, 'aDocument', None),
        ('createDocumentType',):       ('aDocType', 'uri:public', 'uri:system'),
    }

    def checkAppendChild(self):
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]

        if self.node.nodeType == Node.DOCUMENT_NODE:
            # To create a better test for Document Nodes, we remove the
            # documentElement Node. 
            self.node.removeChild(self.node.documentElement)

            # Since appending a Document Type Node to a Document Node isn't
            # allowed, we remove it from the allowed children list for now.
            allowedChildren = allowedChildren[:-1]

        # We add Document Fragment to the list if any child is allowed. Document
        # Fragments can also hold such children. This test only adds empty
        # Document Fragments.
        if allowedChildren:
            allowedChildren = allowedChildren + (Node.DOCUMENT_FRAGMENT_NODE,)

        for factoryMethod, factoryArgs in self.nodeCreateMap.items():
            if type(factoryMethod) is type(()): # DOMImplementation methods
                factory = self.implementation
                factoryMethod = factoryMethod[0]
            else:
                factory = self.document

            newNode = apply(getattr(factory, factoryMethod), factoryArgs)
            numberOfChildren = self.node.childNodes.length

            try:
                # We append two different copies to see how we fare. Esp. handy
                # with testing the restrictions of a Document Node
                returnedNode = self.node.appendChild(newNode)
                newNode = apply(getattr(factory, factoryMethod), factoryArgs)
                returnedNode = self.node.appendChild(newNode)

            except xml.dom.HierarchyRequestErr:
                if self.node.nodeType == Node.DOCUMENT_NODE:
                    if newNode.nodeType == Node.ELEMENT_NODE:
                        assert self.node.documentElement, (
                            "Couldn't add a Element node to an empty Document."
                        )
                    else: # tried to append nonElement to a Document node
                        assert newNode.nodeType not in allowedChildren, (
                            "Couldn't append a %s Node." % TYPE_NAME[
                            newNode.nodeType])
                else: # tried to append a node to a nonDocument node
                    assert newNode.nodeType not in allowedChildren, (
                        "Couldn't append a %s Node." % TYPE_NAME[
                        newNode.nodeType])

            except xml.dom.NoModificationAllowedErr:
                assert self.node.nodeType in self.readOnlyNodeList, (
                    "Claim of read-only-ness on a modifiable node. "
                    "Tried to append a %s Node" % TYPE_NAME[newNode.nodeType]
                )

            else:
                if newNode.nodeType != Node.DOCUMENT_FRAGMENT_NODE:
                    assert isSameNode(newNode, returnedNode), (
                        "Returned Node is not the same as has been added.")

                    checkAttributeSameNode(self.node, 'lastChild', newNode)
                    checkLength(self.node.childNodes, numberOfChildren + 2)
                else:
                    checkLength(self.node.childNodes, numberOfChildren)

                assert newNode.nodeType in allowedChildren, (
                    "Was allowed to append a %s Node." % TYPE_NAME[
                        newNode.nodeType])

                assert self.node.nodeType not in self.readOnlyNodeList, (
                    "Was allowed to append a %s Node to a read-only Node" % (
                        TYPE_NAME[newNode.nodeType]))

    def checkAppendChildForeignNode(self):
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        foreignDoc = self.implementation.createDocument(None, 'anotherDoc',
            None)
        if Node.TEXT_NODE in allowedChildren:
            foreignNode = foreignDoc.createTextNode('a Text Node')
        else:
            foreignNode = foreignDoc.createComment('a Comment Node')

        try:
            self.node.appendChild(foreignNode)
        except xml.dom.WrongDocumentErr:
            pass
        else:
            assert 0, (
                "Allowed to add %s Node from a foreign Document." %
                    TYPE_NAME[foreignNode.nodeType])

    def checkAppendChildAncestorNode(self):
        if self.node.nodeType in self.readOnlyNodeList:
            return 

        if Node.ELEMENT_NODE not in self.allowedChildrenMap[self.node.nodeType]:
            return
        if self.node.nodeType not in self.allowedChildrenMap[Node.ELEMENT_NODE]:
            return

        ancestorNode = self.document.createElement('foo')
        ancestorNode.appendChild(self.node)

        try:
            self.node.appendChild(ancestorNode)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Allowed to append ancestor Node to a %s Node." % 
                    TYPE_NAME[newNode.nodeType])

    def checkAppendChildSelf(self):
        # See DOM erratum core-6
        if self.node.nodeType in self.readOnlyNodeList:
            return 

        if self.node.nodeType not in self.allowedChildrenMap[
                self.node.nodeType]:
            return

        try:
            self.node.appendChild(self.node)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Allowed to append the Node to itself." % 
                    TYPE_NAME[newNode.nodeType])

    def checkAppendChildWithAttachedNode(self):
        # Test appending a Node that itself is part of a tree
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        if Node.TEXT_NODE in allowedChildren:
            newNode = self.document.createTextNode('a Text Node')
        else:
            newNode = self.document.createComment('a Comment Node')

        oldParent = self.document.createElement('aParentElement')
        oldParent.appendChild(newNode)

        self.node.appendChild(newNode)
        
        assert not oldParent.hasChildNodes(), (
            "Appended Node not removed from previous parent.")

    def checkAppendChildNodeParentReadOnly(self):
        # See DOM erratum core-2 http://www.w3.org/2000/11/DOM-Level-2-errata
        if self.node.nodeType in self.readOnlyNodeList:
            return
        if Node.TEXT_NODE not in self.allowedChildrenMap[self.node.nodeType]:
            return
        if self.node.nodeType in [Node.DOCUMENT_NODE, Node.DOCUMENT_TYPE_NODE]:
            return                      # can't import

        doc = self.parse("""
            <!DOCTYPE doc [
                <!ENTITY entity "Entity text">
            ]>
            <doc/>""")
        # This Text Node has a read-only parent.
        textNode = doc.doctype.entities.getNamedItem('entity').firstChild
        # we need self.node to have the same doc as textNode
        self.node = doc.importNode(self.node, 1)

        try:
            self.node.appendChild(textNode)
        except xml.dom.NoModificationAllowedErr:
            pass
        else:
            assert 0, (
                "Was allowed to append a Node from a read-only tree.")

    def checkRemoveChild(self):
        if self.node.nodeType in self.readOnlyNodeList:
            if self.node.hasChildNodes():
                # Test for read-only
                try:
                    self.node.removeChild(self.node.firstChild)
                except xml.dom.NoModificationAllowedErr:
                    pass
                else:
                    assert 0, "Removed child from read-only Node."
            return

        # If this is a Document Node, let's try and remove the doctype.
        # We actually test this when we have a doctype, as the Document Node
        # created for the Document Node tests doesn't *have* a doctype.
        if self.node.nodeType == Node.DOCUMENT_TYPE_NODE:
            doc = self.implementation.createDocument('', 'foo', self.node)
            try:
                doc.removeChild(doc.doctype)
            except xml.dom.NotFoundErr:
                assert 0, (
                    "The doctype of a Document Node should also be a "
                    "childNode of that Node.")
            except xml.dom.NoModificationAllowedErr:
                pass
            else:
                assert 0, (
                    "Was allowed to remove the doctype of a Document Node.")

        allowedChildren = self.allowedChildrenMap[self.node.nodeType]

        # No use when no children are allowed
        if not allowedChildren:
            return

        if Node.TEXT_NODE in allowedChildren:
            newNode = self.document.createTextNode('a Text Node')
        else:
            newNode = self.document.createComment('a Comment Node')

        self.node.appendChild(newNode)

        returnedNode = self.node.removeChild(newNode)
        assert isSameNode(newNode, returnedNode), (
            "Returned Node is not the appended Node.")
        checkAttribute(newNode, "parentNode", None)
        checkAttribute(returnedNode, "parentNode", None)


    def checkRemoveChildNotFound(self):
        if self.node.nodeType in self.readOnlyNodeList:
            return

        loseNode = self.document.createTextNode('booh')
        try:
            self.node.removeChild(loseNode)
        except xml.dom.NotFoundErr:
            pass
        else:
            assert 0, "Removed a Node that was no longer a child node."

    def checkInsertBefore(self):
        if self.node.nodeType in self.readOnlyNodeList:
            if self.node.hasChildNodes():
                # Test for read-only
                newNode = self.document.createTextNode('a Text Node')
                try:
                    self.node.insertBefore(newNode, self.node.firstChild)
                except xml.dom.NoModificationAllowedErr:
                    pass
                else:
                    assert 0, "Allowed to insert Node into read-only Node."
            return

        allowedChildren = self.allowedChildrenMap[self.node.nodeType]

        if self.node.nodeType == Node.DOCUMENT_NODE:
            # To create a better test for Document Nodes, we remove the
            # documentElement Node. 
            self.node.removeChild(self.node.documentElement)

            # Since inserting a Document Type Node into a Document Node isn't
            # allowed, we remove it from the allowed children list for now.
            allowedChildren = allowedChildren[:-1]
    
        # No use when no children are allowed.
        if not allowedChildren:
            return
        else:
            # Append DOCUMENT_FRAGMENT to alowed, because we'll add an empty
            # fragment.
            allowedChildren = allowedChildren + (Node.DOCUMENT_FRAGMENT_NODE,)

        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        # Now create the reference to insert before.
        self.node.appendChild(refNode)

        for factoryMethod, factoryArgs in self.nodeCreateMap.items():
            if type(factoryMethod) is type(()): # DOMImplementation methods
                factory = self.implementation
                factoryMethod = factoryMethod[0]
            else:
                factory = self.document

            newNode = apply(getattr(factory, factoryMethod), factoryArgs)
            numberOfChildren = self.node.childNodes.length

            try:
                returnedNode = self.node.insertBefore(newNode, refNode)

            except xml.dom.HierarchyRequestErr:
                if self.node.nodeType == Node.DOCUMENT_NODE:
                    if newNode.nodeType == Node.ELEMENT_NODE:
                        assert self.node.documentElement, (
                            "Couldn't add a Element node to an empty Document."
                        )
                    
                assert newNode.nodeType not in allowedChildren, (
                    "Couldn't append a %s Node." % TYPE_NAME[
                        newNode.nodeType])

            except xml.dom.NoModificationAllowedErr:
                assert self.node.nodeType in self.readOnlyNodeList, (
                    "Claim of read-only-ness on a modifiable node. "
                    "Tried to append a %s Node" % TYPE_NAME[self.node.nodeType]
                )

            else:
                if newNode.nodeType != Node.DOCUMENT_FRAGMENT_NODE:
                    assert isSameNode(newNode, returnedNode), (
                        "Returned Node is not the same as has been added.")

                    checkAttributeSameNode(newNode, 'nextSibling', refNode)
                    checkAttributeSameNode(refNode, 'previousSibling', newNode)
                    checkLength(self.node.childNodes, numberOfChildren + 1)
                else:
                    checkLength(self.node.childNodes, numberOfChildren)

                assert newNode.nodeType in allowedChildren, (
                    "Was allowed to insert a %s Node." % TYPE_NAME[
                        newNode.nodeType])

    def checkInsertBeforeNotFound(self):
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        try:
            self.node.insertBefore(refNode, refNode.cloneNode(0))
        except xml.dom.NotFoundErr:
            pass
        else:
            assert 0, "Inserted Node using a document-less Node as reference."

    def checkInsertBeforeExtraElementToDocument(self):
        if self.node.nodeType == Node.DOCUMENT_NODE:
            newNode = self.document.createElement('foo')
            try:
                self.node.insertBefore(newNode, self.node.documentElement)
            except xml.dom.HierarchyRequestErr:
                pass
            else:
                assert 0, (
                    "Was allowed to insert a second Element into a Document "
                    "Node.")

    def checkInsertBeforeForeignNode(self):
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        foreignDoc = self.implementation.createDocument(None, 'anotherDoc',
            None)
        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
            foreignNode = foreignDoc.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')
            foreignNode = foreignDoc.createComment('a Comment Node')

        self.node.appendChild(refNode)

        try:
            self.node.insertBefore(foreignNode, refNode)
        except xml.dom.WrongDocumentErr:
            pass
        else:
            assert 0, (
                "Allowed to insert %s Node from a foreign Document." %
                    TYPE_NAME[foreignNode.nodeType])

    def checkInsertBeforeAncestorNode(self):
        if self.node.nodeType in self.readOnlyNodeList:
            return 

        if Node.ELEMENT_NODE not in self.allowedChildrenMap[self.node.nodeType]:
            return
        if self.node.nodeType not in self.allowedChildrenMap[Node.ELEMENT_NODE]:
            return

        ancestorNode = self.document.createElement('foo')
        ancestorNode.appendChild(self.node)

        if Node.TEXT_NODE in self.allowedChildrenMap[self.node.nodeType]:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        self.node.appendChild(refNode)

        try:
            self.node.insertBefore(ancestorNode, refNode)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Allowed to insert ancestor Node into a %s Node." %
                    TYPE_NAME[newNode.nodeType])

    def checkInsertBeforeSelf(self):
        # See DOM erratum core-7
        if self.node.nodeType in self.readOnlyNodeList:
            return 

        if self.node.nodeType not in self.allowedChildrenMap[
                self.node.nodeType]:
            return

        if Node.TEXT_NODE in self.allowedChildrenMap[self.node.nodeType]:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        self.node.appendChild(refNode)

        try:
            self.node.insertBefore(self.node, refNode)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Allowed to insert the Node into itself." % 
                    TYPE_NAME[newNode.nodeType])

    def checkInsertBeforeWithAttachedNode(self):
        # Test appending a Node that itself is part of a tree
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
            newNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')
            newNode = self.document.createComment('a Comment Node')

        oldParent = self.document.createElement('aParentElement')
        oldParent.appendChild(newNode)

        self.node.appendChild(refNode)
        self.node.insertBefore(newNode, refNode)
        
        assert not oldParent.hasChildNodes(), (
            "Appended Node not removed from previous parent.")

    def checkInsertBeforeNodeParentReadOnly(self):
        # See DOM erratum core-2 http://www.w3.org/2000/11/DOM-Level-2-errata
        if self.node.nodeType in self.readOnlyNodeList:
            return
        if Node.TEXT_NODE not in self.allowedChildrenMap[self.node.nodeType]:
            return
        if self.node.nodeType in [Node.DOCUMENT_NODE, Node.DOCUMENT_TYPE_NODE]:
            return                      # can't import

        doc = self.parse("""
            <!DOCTYPE doc [
                <!ENTITY entity "Entity text">
            ]>
            <doc/>""")
        # This Text Node has a read-only parent.
        textNode = doc.doctype.entities.getNamedItem('entity').firstChild

        refNode = doc.createTextNode('a Text Node')
        # we need self.node to have the same doc as textNode
        self.node = doc.importNode(self.node, 1)
        self.node.appendChild(refNode)
        
        try:
            self.node.insertBefore(textNode, refNode)
        except xml.dom.NoModificationAllowedErr:
            pass
        else:
            assert 0, (
                "Was allowed to insert a Node from a read-only tree.")

    def checkReplaceChild(self):
        if self.node.nodeType in self.readOnlyNodeList:
            if self.node.hasChildNodes():
                # Test for read-only
                newNode = self.document.createTextNode('a Text Node')
                try:
                    self.node.replaceChild(newNode, self.node.firstChild)
                except xml.dom.NoModificationAllowedErr:
                    pass
                else:
                    assert 0, "Allowed to replace childNode of read-only Node."
            return

        allowedChildren = self.allowedChildrenMap[self.node.nodeType]

        if self.node.nodeType == Node.DOCUMENT_NODE:
            # To create a better test for Document Nodes, we remove the
            # documentElement Node. 
            self.node.removeChild(self.node.documentElement)

            # Since replacing with a Document Type Node on a Document Node isn't
            # allowed, we remove it from the allowed children list for now.
            allowedChildren = allowedChildren[:-1]
    
        # No use when no children are allowed.
        if not allowedChildren:
            return
        else:
            # Append DOCUMENT_FRAGMENT to alowed, because we'll use an empty
            # fragment.
            allowedChildren = allowedChildren + (Node.DOCUMENT_FRAGMENT_NODE,)

        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        for factoryMethod, factoryArgs in self.nodeCreateMap.items():
            if type(factoryMethod) is type(()): # DOMImplementation methods
                factory = self.implementation
                factoryMethod = factoryMethod[0]
            else:
                factory = self.document

            # Now create the reference to replace. We do this for every Node
            # type we try and replace with.
            self.node.appendChild(refNode)

            newNode = apply(getattr(factory, factoryMethod), factoryArgs)
            numberOfChildren = self.node.childNodes.length

            try:
                returnedNode = self.node.replaceChild(newNode, refNode)

            except xml.dom.HierarchyRequestErr:
                if self.node.nodeType == Node.DOCUMENT_NODE \
                   and newNode.nodeType == Node.ELEMENT_NODE:
                    assert self.node.documentElement, \
                           "Couldn't add an Element node to an empty Document."
                    
                assert newNode.nodeType not in allowedChildren, (
                    "Couldn't replace an old Node with a %s Node." % TYPE_NAME[
                        newNode.nodeType])

            except xml.dom.NoModificationAllowedErr:
                assert self.node.nodeType in self.readOnlyNodeList, (
                    "Claim of read-only-ness on a modifiable node. "
                    "Tried to replace an old Node with a %s Node" % TYPE_NAME[
                        self.node.nodeType]
                )

            else:
                if newNode.nodeType != Node.DOCUMENT_FRAGMENT_NODE:
                    assert isSameNode(refNode, returnedNode), (
                        "Returned Node is not the same as has been replaced.")

                    checkAttributeSameNode(self.node, 'lastChild', newNode)
                    checkLength(self.node.childNodes, numberOfChildren)
                    assert isSameNode(refNode, returnedNode), (
                        "Returned Node is not the same as has been replaced.")
                else:
                    checkLength(self.node.childNodes, numberOfChildren - 1)

                assert newNode.nodeType in allowedChildren, (
                    "Was allowed to replace an old Node with a "
                    "%s Node." % TYPE_NAME[newNode.nodeType])

    def checkReplaceChildNotFound(self):
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        try:
            self.node.replaceChild(refNode, refNode.cloneNode(0))
        except xml.dom.NotFoundErr:
            pass
        else:
            assert 0, "Replaced perentless Node (reference had no parent)."

    def checkReplaceChildExtraElementToDocument(self):
        if self.node.nodeType == Node.DOCUMENT_NODE:
            refNode = self.document.createComment('a Comment Node')
            self.node.appendChild(refNode)
            newNode = self.document.createElement('foo')

            try:
                self.node.replaceChild(newNode, refNode)
            except xml.dom.HierarchyRequestErr:
                pass
            else:
                assert 0, (
                    "Was allowed to replace an old Node with a second Element "
                    "into a Document Node.")

    def checkReplaceChildForeignNode(self):
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        foreignDoc = self.implementation.createDocument(None, 'anotherDoc',
            None)
        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
            foreignNode = foreignDoc.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')
            foreignNode = foreignDoc.createComment('a Comment Node')

        self.node.appendChild(refNode)

        try:
            self.node.replaceChild(foreignNode, refNode)
        except xml.dom.WrongDocumentErr:
            pass
        else:
            assert 0, (
                "Allowed to replace an old Node with a %s Node from a "
                "foreign Document." % TYPE_NAME[foreignNode.nodeType])

    def checkReplaceChildAncestorNode(self):
        if self.node.nodeType in self.readOnlyNodeList:
            return 

        if Node.ELEMENT_NODE not in self.allowedChildrenMap[self.node.nodeType]:
            return
        if self.node.nodeType not in self.allowedChildrenMap[Node.ELEMENT_NODE]:
            return

        ancestorNode = self.document.createElement('foo')
        ancestorNode.appendChild(self.node)

        if Node.TEXT_NODE in self.allowedChildrenMap[self.node.nodeType]:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        self.node.appendChild(refNode)

        try:
            self.node.replaceChild(ancestorNode, refNode)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Allowed to replace an old Node with a ancestor Node.")

    def checkReplaceChildSelf(self):
        # See DOM erratum core-8
        if self.node.nodeType in self.readOnlyNodeList:
            return 

        if self.node.nodeType not in self.allowedChildrenMap[
                self.node.nodeType]:
            return

        if Node.TEXT_NODE in self.allowedChildrenMap[self.node.nodeType]:
            refNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')

        self.node.appendChild(refNode)

        try:
            self.node.replaceChild(self.node, refNode)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Allowed to replace an old child Node with the parent Node "
                "itself.")

    def checkReplaceChildWithAttachedNode(self):
        # Test replacing with a Node that itself is part of a tree
        allowedChildren = self.allowedChildrenMap[self.node.nodeType]
        
        # No use when no children are allowed or read-only
        if not allowedChildren or self.node.nodeType in self.readOnlyNodeList:
            return

        if Node.TEXT_NODE in allowedChildren:
            refNode = self.document.createTextNode('a Text Node')
            newNode = self.document.createTextNode('a Text Node')
        else:
            refNode = self.document.createComment('a Comment Node')
            newNode = self.document.createComment('a Comment Node')

        oldParent = self.document.createElement('aParentElement')
        oldParent.appendChild(newNode)

        self.node.appendChild(refNode)
        self.node.replaceChild(newNode, refNode)
        
        assert not oldParent.hasChildNodes(), (
            "Replacing Node not removed from previous parent.")

    def checkReplaceChildNodeParentReadOnly(self):
        # See DOM erratum core-2 http://www.w3.org/2000/11/DOM-Level-2-errata
        if self.node.nodeType in self.readOnlyNodeList:
            return
        if Node.TEXT_NODE not in self.allowedChildrenMap[self.node.nodeType]:
            return
        if self.node.nodeType in [Node.DOCUMENT_NODE, Node.DOCUMENT_TYPE_NODE]:
            return                      # can't import

        doc = self.parse("""
            <!DOCTYPE doc [
                <!ENTITY entity "Entity text">
            ]>
            <doc/>""")
        # This Text Node has a read-only parent.
        textNode = doc.doctype.entities.getNamedItem('entity').firstChild
        # we need self.node to have the same doc as textNode
        self.node = doc.importNode(self.node, 1)
        refNode = doc.createTextNode('a Text Node')
        self.node.appendChild(refNode)
        
        try:
            self.node.replaceChild(textNode, refNode)
        except xml.dom.NoModificationAllowedErr:
            pass
        else:
            assert 0, (
                "Was allowed to replace a Node with a Node from a read-only "
                "tree.")



# --- Document

class DocumentReadTestCase(NodeReadTestCaseBase):

    def setUp(self):
        self.createDocument()
        self.node = self.document
        self.expectedType = Node.DOCUMENT_NODE

    def checkGetDoctype(self):
        checkAttribute(self.document, "doctype", None)
        checkReadOnly(self.document, "doctype")

    def checkGetImplementation(self):
        checkAttribute(self.document, "implementation", self.implementation)
        checkReadOnly(self.document, "implementation")

    def checkGetDocumentElement(self):
        checkAttributeNot(self.document, "documentElement", None)
        checkReadOnly(self.document, "documentElement")

    def checkCloneNode(self):
        # TODO: Implementation dependent, what should we test?
        pass


class DocumentWriteTestCase(NodeWriteTestCaseBase):    

    def setUp(self):
        self.node = self.createDocument()

    def checkGetElementsByTagName(self):
        doc = self.document

        elements = {}
        names = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
        for c in names:
            elements[c] = doc.createElement(c)
        # set up simple tree
        elements['a'].appendChild(elements['b'])
        elements['a'].appendChild(elements['d'])
        elements['b'].appendChild(elements['c'])
        elements['d'].appendChild(elements['e'])
        elements['d'].appendChild(elements['h'])
        elements['e'].appendChild(elements['f'])
        elements['e'].appendChild(elements['g'])
        elements['h'].appendChild(elements['i'])

        doc.documentElement.appendChild(elements['a'])
        
        # now test

        # find all elements in the right order
        result = doc.getElementsByTagName('*')
        assert len(result) == len(names) + 1
        for name, element in map(None, names, result[1:]):
            assert name == element.tagName

        # find single element, top
        result = doc.getElementsByTagName('a')
        assert len(result) == 1
        assert result[0].tagName == 'a'

        # find single element somewhere in tree
        result = doc.getElementsByTagName('h')
        assert len(result) == 1
        assert result[0].tagName == 'h'

    def checkCreateAttribute(self):
        attr = self.document.createAttribute(self.TEST_NAME)

        checkAttribute(attr, 'nodeType', Node.ATTRIBUTE_NODE)
        checkAttribute(attr, 'name', self.TEST_NAME)
        checkAttribute(attr, 'localName', None)
        checkAttribute(attr, 'prefix', None)
        checkAttribute(attr, 'namespaceURI', None)
        checkAttribute(attr, 'value', '')
        checkAttributeSameNode(attr, 'ownerDocument', self.document)

        # Note the ':' in the name, createAttribute does not know about
        # namespaces, so the colon has no special meaning.
        attr = self.document.createAttribute('not_a_prefix:not_a_localname')
        checkAttribute(attr, 'name', 'not_a_prefix:not_a_localname')
        checkAttribute(attr, 'localName', None)
        checkAttribute(attr, 'prefix', None)

        try:
            self.document.createAttribute('5_illegal')
        except xml.dom.InvalidCharacterErr:
            pass
        else:
            assert 0, "Created Attribute Node with illegal name."

    def checkCreateCDATASection(self):
        cdata = self.document.createCDATASection('A CDATA Section')

        checkAttribute(cdata, 'nodeType', Node.CDATA_SECTION_NODE)
        checkAttribute(cdata, 'data', 'A CDATA Section')
        checkAttributeSameNode(cdata, 'ownerDocument', self.document)

    def checkCreateComment(self):
        comment = self.document.createComment('A Comment')

        checkAttribute(comment, 'nodeType', Node.COMMENT_NODE)
        checkAttribute(comment, 'data', 'A Comment')
        checkAttributeSameNode(comment, 'ownerDocument', self.document)

    def checkCreateDocumentFragment(self):
        fragment = self.document.createDocumentFragment()

        checkAttribute(fragment, 'nodeType', Node.DOCUMENT_FRAGMENT_NODE)
        checkLength(fragment.childNodes, 0)
        checkAttributeSameNode(fragment, 'ownerDocument', self.document)

    def checkCreateElement(self):
        el = self.document.createElement(self.TEST_NAME)

        checkAttribute(el, 'nodeType', Node.ELEMENT_NODE)
        checkAttribute(el, 'tagName', self.TEST_NAME)
        checkAttribute(el, 'localName', None)
        checkAttribute(el, 'prefix', None)
        checkAttribute(el, 'namespaceURI', None)
        checkAttributeSameNode(el, 'ownerDocument', self.document)

        # Note the ':' in the name, createElement does not know about
        # namespaces, so the colon has no special meaning.
        el = self.document.createElement('not_a_prefix:not_a_localname')
        checkAttribute(el, 'tagName', 'not_a_prefix:not_a_localname')
        checkAttribute(el, 'localName', None)
        checkAttribute(el, 'prefix', None)

    def checkCreateElementInvalidCharacter(self):
        try:
            self.document.createElement('5_illegal')
        except xml.dom.InvalidCharacterErr:
            pass
        else:
            assert 0, "Created Element Node with illegal name."

    def checkCreateEntityReference(self):
        entRef = self.document.createEntityReference('entityReference')

        checkAttribute(entRef, 'nodeType', Node.ENTITY_REFERENCE_NODE)
        checkAttribute(entRef, 'nodeName', 'entityReference')
        checkAttributeSameNode(entRef, 'ownerDocument', self.document)

    def checkCreateEntityReferenceInvalidCharacter(self):
        try:
            self.document.createEntityReference('5_illegal')
        except xml.dom.InvalidCharacterErr:
            pass
        else:
            assert 0, "Created Entity Reference Node with illegal name."

    def checkCreateProcessingInstruction(self):
        pi = self.document.createProcessingInstruction('PITarget', 'PI Data')

        checkAttribute(pi, 'nodeType', Node.PROCESSING_INSTRUCTION_NODE)
        checkAttribute(pi, 'target', 'PITarget')
        checkAttribute(pi, 'data', 'PI Data')
        checkAttributeSameNode(pi, 'ownerDocument', self.document)

    def checkCreateProcessingInstructionInvalidCharacter(self):
        try:
            self.document.createProcessingInstruction('5_illegal', 'data')
        except xml.dom.InvalidCharacterErr:
            pass
        else:
            assert 0, "Created Processing Instruction Node with illegal name."

    def checkCreateTextNode(self):
        text = self.document.createTextNode('A Text Node')

        checkAttribute(text, 'nodeType', Node.TEXT_NODE)
        checkAttribute(text, 'data', 'A Text Node')
        checkAttributeSameNode(text, 'ownerDocument', self.document)


# --- Element

class ElementReadTestCase(NodeReadTestCaseBase):

    def setUp(self):
        doc = self.createDocument()
        self.element = self.node = doc.createElement("per")
        self.expectedType = Node.ELEMENT_NODE
        self.floating_element = self.element
        self.attached_element = doc.createElement("attached")
        doc.documentElement.appendChild(self.attached_element)

    def checkDocumentElementChildNodes(self):
        checkLength(self.document.documentElement.childNodes, 1)

    def checkAttachedElementParentNode(self):
        checkAttributeSameNode(self.attached_element, "parentNode",
                               self.document.documentElement)

    def checkDocumentElementFirstChild(self):
        checkAttributeSameNode(self.document.documentElement, "firstChild",
                               self.attached_element)
        checkAttributeSameNode(
            self.document.documentElement.firstChild, "parentNode",
                               self.document.documentElement)

    def checkTagName(self):
        checkAttribute(self.floating_element, "tagName", "per")
        checkAttribute(self.attached_element, "tagName", "attached")
        checkReadOnly(self.floating_element, "tagName")

    def checkGetAttribute(self):
        assert self.element.getAttribute("ugga") == "", \
               "non-existant attribute should return ''"

    def checkGetAttributeNode(self):
        assert self.element.getAttributeNode("ugga") is None, \
               "non-existant attribute should return None"

    def checkCloneNode(self):
        el = self.element
        doc = self.document
        el.appendChild(doc.createTextNode('A Text Node'))
        el.appendChild(doc.createComment('A Comment'))
        el.setAttribute('attr1', 'An attribute')
        el.setAttribute('attr2', 'Another attribute')
        
        el.appendChild(el.cloneNode(0))
        clone = el.cloneNode(1)

        assert not isSameNode(el, clone), "Clone is same Node as original."
        assert not isSameNode(el, clone.lastChild), (
            "Clone is same Node as original.")

        checkAttribute(clone, 'parentNode', None)
        checkLength(clone.childNodes, el.childNodes.length)
        checkLength(clone.attributes, el.attributes.length)
        checkLength(clone.lastChild.childNodes, 0)
        checkLength(clone.lastChild.attributes, el.attributes.length)

        checkAttribute(clone, 'nodeName', el.nodeName)
        checkAttribute(clone.lastChild, 'nodeName', el.nodeName)
        checkAttribute(clone, 'nodeType', el.nodeType)
        checkAttribute(clone.lastChild, 'nodeType', el.nodeType)
        checkAttribute(clone, 'nodeValue', el.nodeValue)
        checkAttribute(clone.lastChild, 'nodeValue', el.nodeValue)

        for i in range(clone.childNodes.length):
            checkAttribute(clone.childNodes.item(i), 'nodeType',
                el.childNodes.item(i).nodeType)
            if clone.childNodes.item(i).nodeType != Node.ELEMENT_NODE:
                checkAttribute(clone.childNodes.item(i), 'data',
                    el.childNodes.item(i).data)

        for i in range(clone.attributes.length):
            checkAttribute(clone.attributes.item(i), 'name',
                el.attributes.item(i).name)
            checkAttribute(clone.lastChild.attributes.item(i), 'name',
                el.attributes.item(i).name)

            checkAttribute(clone.attributes.item(i), 'value',
                el.attributes.item(i).value)
            checkAttribute(clone.lastChild.attributes.item(i), 'value',
                el.attributes.item(i).value)

            # deep and shallow clones should clone attributes
            value = clone.attributes.item(i).value
            el.attributes.item(i).value = 'spam'
            checkAttribute(clone.attributes.item(i), 'value', value)
            checkAttribute(clone.lastChild.attributes.item(i), 'value', value)
            
            checkAttributeSameNode(clone.attributes.item(i), 'ownerElement',
                clone)
            checkAttributeSameNode(clone.lastChild.attributes.item(i),
                'ownerElement', clone.lastChild)

            assert not isSameNode(el.attributes.item(i),
                                  clone.attributes.item(i))
           

class ElementWriteTestCase(NodeWriteTestCaseBase):

    def setUp(self):
        doc = self.createDocument()
        self.element = self.node = doc.createElement("per")
        self.floating_element = self.element
        self.attached_element = doc.createElement("attached")
        self.attached_element2 = doc.createElement("attached2")
        doc.documentElement.appendChild(self.attached_element)
        doc.documentElement.appendChild(self.attached_element2)

    def checkAttachedElementsNextSibling(self):
        checkAttributeSameNode(self.attached_element, "nextSibling",
                               self.attached_element2)
        checkAttributeSameNode(self.attached_element2, "nextSibling",
                               None)

    def checkAttachedElementsPreviousSibling(self):
        checkAttributeSameNode(self.attached_element, "previousSibling",
                               None)
        checkAttributeSameNode(self.attached_element2, "previousSibling",
                               self.attached_element)

    def checkGetElementsByTagName(self):
        doc = self.document
        el = self.element

        elements = {}
        names = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
        for c in names:
            elements[c] = doc.createElement(c)
        # set up simple tree
        elements['a'].appendChild(elements['b'])
        elements['a'].appendChild(elements['d'])
        elements['b'].appendChild(elements['c'])
        elements['d'].appendChild(elements['e'])
        elements['d'].appendChild(elements['h'])
        elements['e'].appendChild(elements['f'])
        elements['e'].appendChild(elements['g'])
        elements['h'].appendChild(elements['i'])

        el.appendChild(elements['a'])
        
        # now test

        # find all elements in the right order
        result = el.getElementsByTagName('*')
        assert len(result) == len(names)
        for name, element in map(None, names, result):
            assert name == element.tagName

        # find single element, top
        result = el.getElementsByTagName('a')
        assert len(result) == 1
        assert result[0].tagName == 'a'

        # find single element somewhere in tree
        result = el.getElementsByTagName('h')
        assert len(result) == 1
        assert result[0].tagName == 'h'

    def checkSetAttribute(self):
        self.element.setAttribute("ugga", "foo")

        assert self.element.hasAttribute("ugga"), \
               "Test for presence of created attribute returned false."

        value = self.element.getAttribute("ugga")
        assert value == 'foo', \
               ("Incorrect attr value returned. Expected 'foo', got %s"
                % repr(value))

    def checkSetAttributeIllegalCharacter(self):
        try:
            self.element.setAttribute('5_illegal', "Don't eat this")
        except xml.dom.InvalidCharacterErr:
            pass
        else:
            assert 0, "Was allowed to use an illegal attribute name."

    def checkSetAttributeNode(self):
        node = self.document.createAttribute("ugga")
        node.value = 'foo'
        returnValue = self.element.setAttributeNode(node)

        assert self.element.hasAttribute("ugga"), \
               "Test for presence of created attribute returned false."
        assert returnValue is None, "Returned value is %s" % repr(returnValue)

        value = self.element.getAttribute("ugga")
        assert value == 'foo', \
               ("Incorrect attr value returned. Expected 'foo', got %s"
                % repr(value))

        returnedNode = self.element.getAttributeNode("ugga")
        assert isSameNode(node, returnedNode), \
               "Incorrect node returned from getAttributeNode."

    def checkSetAttributeNodeReplaceExisting(self):
        node = self.document.createAttribute("ugga")
        self.element.setAttributeNode(node)
        newNode = self.document.createAttribute("ugga")

        returnValue = self.element.setAttributeNode(newNode)
        if returnValue is None:
            assert 0, "setAttributeNode did not replace original attribute"
        assert isSameNode(node, returnValue), (
            "setAttributeNode returned %s" % repr(returnValue))

    def checkSetAttributeNodeWrongDocument(self):
        foreignDoc = self.implementation.createDocument(None, 'foo', None)
        foreignAttr = foreignDoc.createAttribute('spam')
        try:
            self.element.setAttributeNodeNS(foreignAttr)
        except xml.dom.WrongDocumentErr:
            pass
        else:
            assert 0, "Was allowed to setAttributeNode with a foreign Attr."

    def checkSetAttributeNodeAlreadyInUse(self):
        otherElement = self.document.createElement('foo')
        otherAttr = self.document.createAttribute('spam')
        otherElement.setAttributeNodeNS(otherAttr)
        try:
            self.element.setAttributeNodeNS(otherAttr)
        except xml.dom.InuseAttributeErr:
            pass
        else:
            assert 0, (
                "Was allowed to setAttributeNode with an Attr already in use.")

    def checkRemoveAttribute(self):
        node1 = self.document.createAttribute("foo")
        node2 = self.document.createAttribute("bar")
        self.element.setAttributeNode(node1)
        self.element.setAttributeNode(node2)

        self.element.removeAttribute("foo")

        assert not self.element.hasAttribute("foo"), \
               "Test for presence of created attribute still returns true."

        assert self.element.hasAttribute("bar"), \
               "Test for presence of created attribute returned false."

        checkAttribute(node1, 'ownerElement', None)
        
    def checkRemoveAttributeNode(self):
        node1 = self.document.createAttribute("foo")
        node2 = self.document.createAttribute("bar")
        self.element.setAttributeNode(node1)
        self.element.setAttributeNode(node2)

        returnedNode = self.element.removeAttributeNode(node1)

        assert isSameNode(node1, returnedNode), (
            "Returned node not the same as the one removed.")

        checkAttribute(node1, 'ownerElement', None)

        assert not self.element.hasAttribute("foo"), (
               "Test for presence of created attribute still returns true.")

        assert self.element.hasAttribute("bar"), (
               "Test for presence of created attribute returned false.")

    def checkRemoveAttributeNodeNotFound(self):
        node = self.document.createAttribute("foo")
        try:
            self.element.removeAttributeNode(node)
        except xml.dom.NotFoundErr:
            pass
        else:
            assert 0, (
                "No NotFoundErr raised when trying to remove "
                "non-attached Node.")


# --- CharacterData

class CharacterDataReadTestCaseBase(NodeReadTestCaseBase):

    def checkGetData(self):
        checkAttribute(self.chardata, "data", "com")

    def checkGetLength(self):
        checkLength(self.chardata, 3)
        assert len(self.chardata.data) == 3
        assert len(self.chardata._get_data()) == 3

    def checkSubstringData(self):
        assert self.chardata.substringData(0, 2) == "co"

    def checkSubstringDataNegativeOffset(self):
        try:
            self.chardata.substringData(-2, 0)
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative offset."

    def checkSubstringDataOffsetGreaterThanLength(self):
        try:
           self.chardata.substringData(10, 0) 
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for too high offset."

    def checkSubstringDataNegativeCount(self):
        try:
            self.chardata.substringData(0, -2)
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative count."

    def checkSubstringDataOffsetAndCountGreaterThanLength(self):
        assert self.chardata.substringData(1, 10) == "om"

    def checkCloneNode(self):
        clone = self.chardata.cloneNode(0)
        deepClone = self.chardata.cloneNode(1)

        assert not isSameNode(self.chardata, clone), (
            "Clone is same as original.")
        assert not isSameNode(self.chardata, deepClone), (
            "Clone is same as original.")
        
        checkAttribute(clone, 'parentNode', None)
        checkAttribute(deepClone, 'parentNode', None)
        checkAttribute(clone, 'nodeType', self.chardata.nodeType)
        checkAttribute(deepClone, 'nodeType', self.chardata.nodeType)
        checkAttribute(clone, 'data', self.chardata.data)
        checkAttribute(deepClone, 'data', self.chardata.data)
        checkLength(clone.childNodes, 0)
        checkLength(deepClone.childNodes, 0)


class CharacterDataWriteTestCaseBase(NodeWriteTestCaseBase):

    def checkSetData(self):
        self.chardata._set_data("data")
        checkAttribute(self.chardata, "data", "data")

    def checkAppendData(self):
        self.chardata.appendData("com")
        checkAttribute(self.chardata, "data", "comcom")

    def checkInsertData(self):
        self.chardata.insertData(2, "com")
        checkAttribute(self.chardata, "data", "cocomm")

    def checkInsertDataNegativeOffset(self):
        try:
            self.chardata.insertData(-2, 'foo')
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative offset."

    def checkInsertDataOffsetGreaterThanLength(self):
        try:
           self.chardata.insertData(10, 'foo') 
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for too high offset."

    def checkDeleteData(self):
        self.chardata.deleteData(1, 1)
        checkAttribute(self.chardata, "data", "cm")

    def checkDeleteDataNegativeOffset(self):
        try:
            self.chardata.deleteData(-2, 0)
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative offset."

    def checkDeleteDataOffsetGreaterThanLength(self):
        try:
           self.chardata.deleteData(10, 0) 
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for too high offset."

    def checkDeleteDataNegativeCount(self):
        try:
            self.chardata.deleteData(0, -2)
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative count."

    def checkDeleteDataOffsetAndCountGreaterThanLength(self):
        self.chardata.deleteData(0, 10)
        checkAttribute(self.chardata, "data", "")

    def checkReplaceData(self):
        self.chardata.replaceData(1, 3, "uuuu")
        checkAttribute(self.chardata, "data", "cuuuu")

    def checkReplaceDataNegativeOffset(self):
        try:
            self.chardata.replaceData(-2, 0, 'foo')
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative offset."

    def checkReplaceDataOffsetGreaterThanLength(self):
        try:
           self.chardata.replaceData(10, 0, 'foo') 
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for too high offset."

    def checkReplaceDataNegativeCount(self):
        try:
            self.chardata.replaceData(0, -2, 'foo')
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative count."

    def checkReplaceDataOffsetAndCountGreaterThanLength(self):
        self.chardata.replaceData(0, 10, "foo")
        checkAttribute(self.chardata, "data", "foo")


# --- Comment

class CommentReadTestCase(CharacterDataReadTestCaseBase):

    def setUp(self):
        self.chardata = self.node = self.createDocument().createComment("com")
        self.expectedType = Node.COMMENT_NODE


class CommentWriteTestCase(CharacterDataWriteTestCaseBase):
    
    def setUp(self):
        self.chardata = self.node = self.createDocument().createComment("com")


# --- Text

class TextReadTestCase(CharacterDataReadTestCaseBase):

    def setUp(self):
        self.chardata = self.node = self.createDocument().createTextNode("com")
        self.expectedType = Node.TEXT_NODE


class TextWriteTestCase(CharacterDataWriteTestCaseBase):

    def setUp(self):
        self.chardata = self.node = self.createDocument().createTextNode("com")

    def checkAcquisition(self):
        if 1:
            # This test is disabled.  Simple implicit acquisition
            # causes removeAttribute() to be acquired when it
            # probably should not, but this issue is not of
            # sufficient importance for now.
            return
        cdata = self.document.createTextNode("com")        
        self.document.documentElement.appendChild(cdata)
        cdata2 = self.document.createTextNode("com2")
        self.document.documentElement.appendChild(cdata2)        
        attr = self.document.createAttribute("attrName")
        self.document.documentElement.setAttributeNode(attr)
        # Technically these should raise.  Because of acquisition they
        # might not.  We want to make sure that they don't affect
        # the tree.
        try:
            self.document.documentElement.firstChild.normalize()
        except:
            pass
        try:
            self.document.documentElement.firstChild.removeAttribute(
                "attrName")
        except:
            pass
        else:
            assert 0, 'removeAttribute() was acquired from documentElement.'
        checkAttribute(self.document.documentElement.childNodes, "length", 2)
        checkAttribute(self.document.documentElement.attributes, "length", 1)

    def checkSplitText(self):
        newNode = self.chardata.splitText(2)

        checkAttribute(self.chardata, 'data', 'co')
        checkAttribute(newNode, 'data', 'm')

    def checkSplitTextOffsetEqualToLength(self):
        try:
            newNode = self.chardata.splitText(self.chardata.length)
        except xml.dom.IndexSizeErr:
            assert 0, (
                "INDEX_SIZE_ERR raised on splitText with offset == length.")
        checkAttribute(self.chardata, 'data', 'com')
        checkAttribute(newNode, 'data', '')

    def checkSplitTextNegativeOffset(self):
        try:
           self.chardata.splitText(-2)
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for negative offset."

    def checkSplitTextOffsetGreateThanLength(self):
        try:
           self.chardata.splitText(10) 
        except xml.dom.IndexSizeErr:
            pass
        else:
            assert 0, "Expected exception for too high offset."

    def checkSplitTextWithParent(self):
        el = self.document.createElement('foo')
        el.appendChild(self.chardata)

        newNode = self.chardata.splitText(2)
        checkAttributeSameNode(self.chardata, 'nextSibling', newNode)


# --- Attr

class AttrReadTestCase(NodeReadTestCaseBase):

    def setUp(self):
        self.attr = self.node = self.createDocument().createAttribute("name")
        self.expectedType = Node.ATTRIBUTE_NODE

    def checkGetName(self):
        checkAttribute(self.attr, "name", "name")

    def checkGetSpecified(self):
        assert self.attr._get_specified()
        assert self.attr.specified

    def checkGetValue(self):
        checkAttribute(self.attr, "value", "")

        checkLength(self.attr.childNodes, 1)
        checkAttribute(self.attr.firstChild, 'nodeType', Node.TEXT_NODE)
        checkAttribute(self.attr.firstChild, 'data', '')

    def checkCloneNode(self):
        clone = self.attr.cloneNode(0)
        deepClone = self.attr.cloneNode(1)

        assert not isSameNode(self.attr, clone), "Clone is same as original."
        assert not isSameNode(self.attr, deepClone), (
            "Clone is same as original.")
        
        checkAttribute(clone, 'parentNode', None)
        checkAttribute(deepClone, 'parentNode', None)
        checkAttribute(clone, 'nodeType', self.attr.nodeType)
        checkAttribute(deepClone, 'nodeType', self.attr.nodeType)
        checkAttribute(clone, 'name', self.attr.name)
        checkAttribute(deepClone, 'name', self.attr.name)
        checkAttribute(clone, 'value', self.attr.value)
        checkAttribute(deepClone, 'value', self.attr.value)
        checkAttribute(clone, 'specified', 1)
        checkAttribute(deepClone, 'specified', 1)
        checkAttribute(clone, 'nodeName', self.attr.nodeName)
        checkAttribute(deepClone, 'nodeName', self.attr.nodeName)
        checkAttribute(clone, 'nodeValue', self.attr.nodeValue)
        checkAttribute(deepClone, 'nodeValue', self.attr.nodeValue)

        checkLength(clone.childNodes, 1) # Subtree models value
        checkAttribute(clone.firstChild, 'nodeType', Node.TEXT_NODE)
        checkAttribute(clone.firstChild, 'data', self.attr.value)
        checkLength(deepClone.childNodes, 1)
        checkAttribute(deepClone.firstChild, 'nodeType', Node.TEXT_NODE)
        checkAttribute(deepClone.firstChild, 'data', self.attr.value)

class AttrWriteTestCase(NodeWriteTestCaseBase):

    def setUp(self):
        self.attr = self.node = self.createDocument().createAttribute(
            "attrName")
        self.element = self.document.createElement("eltName")

    def checkSetValue(self):
        self.attr._set_value("14")
        checkAttribute(self.attr, "value", "14")

        checkLength(self.attr.childNodes, 1)
        checkAttribute(self.attr.firstChild, 'nodeType', Node.TEXT_NODE)
        checkAttribute(self.attr.firstChild, 'data', '14')

    def checkManipulateSubTree(self):
        attr = self.attr

        assert attr.hasChildNodes(), "Attr doesn't have subtree to manipulate!"

        newNode = self.document.createTextNode('New Value')
        attr.replaceChild(newNode, attr.firstChild)
        checkAttribute(attr.firstChild, 'data', 'New Value')
        checkAttribute(attr, 'value', 'New Value')

        attr.appendChild(self.document.createTextNode(' (appended)'))
        checkAttribute(attr, 'value', 'New Value (appended)')

        attr.firstChild.appendData(' ')
        attr.replaceChild(self.document.createEntityReference('foo'),
            attr.lastChild)
        checkAttribute(attr, 'value', 'New Value ')

        # Setting the value with a string containing an XML reference does *not*
        # cause it to create an EntiyReference. Everything is a string literal.
        attr.value = 'Some Value &test;'
        checkLength(attr.childNodes, 1)
        checkAttribute(attr.firstChild, 'data', 'Some Value &test;')

    def checkOwnerElement(self):
        checkAttribute(self.attr, "ownerElement", None)
        checkReadOnly(self.attr, "ownerElement")
        self.element.setAttributeNode(self.attr)
        checkAttributeSameNode(self.attr, "ownerElement", self.element)
        checkReadOnly(self.attr, "ownerElement")        

    def checkAttrTwoReferencesIntegrity(self):
        #XXX this test should be generalized to any reference
        attr = self.document.createAttribute('spam')
        self.document.documentElement.setAttributeNode(attr)
        a1 = self.document.documentElement.attributes.item(0)
        a2 = self.document.documentElement.attributes.item(0)
        a1.appendChild(self.document.createTextNode('eggs'))
        assert a1.childNodes.length == a2.childNodes.length, \
               "Write to one attr reference isn't reflected in another ref."

    def checkAttrReferenceElementAttributeIntegrity(self):
        "changing an attribute node should be reflected by getAttribute"
        attr = self.document.createAttribute('spam')
        self.document.documentElement.setAttributeNode(attr)
        a1 = self.document.documentElement.attributes.item(0)
        a1.value = 'eggs'
        assert (self.document.documentElement.getAttribute('spam') ==
                'eggs'), (
            "setting value of attr reference isn't reflected by getAttribute")
        a1.appendChild(self.document.createTextNode('ham'))
        assert (self.document.documentElement.getAttribute('spam') ==
                'eggsham'), (
            "appendChild on attr reference isn't reflected by getAttribute.")
        
    def checkSetAttrWithSubtree(self):
        "setAttributeNode shouldn't lose attr subtree information"
        eggs = self.document.createTextNode('eggs')
        ham = self.document.createTextNode('ham')       
        self.attr.appendChild(eggs)
        self.attr.appendChild(ham)
        self.element.setAttributeNode(self.attr)
        # check that getting the attr from the element preserves subtree
        checkAttribute(self.element.attributes.item(0).childNodes, "length", 3)
        assert self.attr.firstChild.nextSibling.isSameNode(eggs), (
            "setting an attribute node destroys children")            
        assert self.attr.firstChild.nextSibling.nextSibling.isSameNode(ham), (
            "setting an attribute node destroys children")
        # check that another ref preserves subtree, too
        attr2 = self.element.getAttributeNode('attrName')
        checkAttribute(attr2.childNodes, "length", 3)
        assert attr2.firstChild.nextSibling.isSameNode(eggs), (        
            "setting an attribute node destroys children")            
        assert attr2.firstChild.nextSibling.nextSibling.isSameNode(ham), (
            "setting an attribute node destroys children")            

    def checkCloneNode(self):
        attr = self.attr
        clone = attr.cloneNode(0)

        assert not isSameNode(attr, clone), "Clone is same Node as original."
        checkAttribute(clone, 'value', attr.value)
        
        # make sure the cloned attr isn't sharing data with the original
        oldValue = self.attr.value
        self.attr.value = 'spam'
        checkAttribute(clone, 'value', oldValue)
        
# --- Default attributes

class DefaultAttrTestCase(TestCaseBase):

    def setUp(self):
        self.document = self.parse("""
            <!DOCTYPE doc [
                <!ELEMENT doc EMPTY>
                <!ATTLIST doc foo CDATA "bar">
            ]>
            <doc/>
        """)

    def checkHasAttribute(self):
        el = self.document.documentElement
        
        assert el.hasAttribute('foo'), 'Default attribute not found.'

    def checkGetAttribute(self):
        el = self.document.documentElement
        
        assert el.getAttribute('foo') == 'bar', (
            "Wrong value of default attribute found, expected 'bar', found %s" %
            repr(el.getAttribute('foo')))

    def checkCreateElement(self):
        el = self.document.createElement('doc')

        assert el.hasAttribute('foo'), (
            'Newly created Element Node should have default attribute.')
        assert el.getAttribute('foo') == 'bar', (
            "Wrong value of default attribute found, expected 'bar', found %s" %
            el.getAttribute('foo'))
        checkAttribute(el.getAttributeNode('foo'), 'specified', 0)

    def checkCloneNode(self):
        attr = self.document.documentElement.getAttributeNode('foo')
        clone = attr.cloneNode(0)
        checkAttribute(clone, 'specified', 1)
    
    def checkCloneNodeElement(self):
        clone = self.document.documentElement.cloneNode(0)
        checkAttribute(clone.getAttributeNode('foo'), 'specified', 0)

        # XXX also check that cloned attrs aren't the same nodes, changes
        # aren't reflected across refs

        
    def checkRemoveAttribute(self):
        el = self.document.documentElement
        # Replace default with specified attr
        el.setAttribute('foo', 'baz')

        el.removeAttribute('foo')
        assert el.hasAttribute('foo'), (
            'Removing specified attribute should bring back default attribute.')
        assert el.getAttribute('foo') == 'bar', (
            "Wrong value of default attribute found, expected 'bar', found %s" %
            el.getAttribute('foo'))
        checkAttribute(el.getAttributeNode('foo'), 'specified', 0)

    def checkRemoveAttributeNode(self):
        el = self.document.documentElement
        newAttr = self.document.createAttribute('foo')
        newAttr.value = 'baz'
        # Replace default with specified attr
        el.setAttributeNode(newAttr)

        el.removeAttributeNode(newAttr)
        assert el.hasAttribute('foo'), (
            'Removing specified attribute should bring back default attribute.')
        assert el.getAttribute('foo') == 'bar', (
            "Wrong value of default attribute found, expected 'bar', found %s" %
            el.getAttribute('foo'))
        checkAttribute(el.getAttributeNode('foo'), 'specified', 0)

    def checkRemoveNamedItem(self):
        el = self.document.documentElement
        # Replace default with specified attr
        el.setAttribute('foo', 'baz')

        el.attributes.removeNamedItem('foo')
        assert el.hasAttribute('foo'), (
            'Removing specified attribute should bring back default attribute.')
        assert el.getAttribute('foo') == 'bar', (
            "Wrong value of default attribute found, expected 'bar', found %s" %
            el.getAttribute('foo'))
        checkAttribute(el.getAttributeNode('foo'), 'specified', 0)

    def checkSpecified(self):
        checkAttribute(self.document.documentElement.getAttributeNode('foo'),
            'specified', 0)

    def checkSetAttribute(self):
        el = self.document.documentElement
        el.setAttribute('foo', 'baz')
        checkAttribute(el.getAttributeNode('foo'), 'specified', 1)

    def checkSetAttributeNode(self):
        el = self.document.documentElement
        newAttr = self.document.createAttribute('foo')
        newAttr.value = 'baz'
        el.setAttributeNode(newAttr)
        checkAttribute(el.getAttributeNode('foo'), 'specified', 1)


# --- DocumentFragment

class DocumentFragmentReadTestCase(NodeReadTestCaseBase):

    def setUp(self):
        self.docfrag = self.createDocument().createDocumentFragment()
        self.node = self.docfrag
        self.expectedType = Node.DOCUMENT_FRAGMENT_NODE

    def checkCloneNode(self):
        frag = self.docfrag

        frag.appendChild(self.document.createComment('foo'))
        frag.appendChild(self.document.createTextNode('bar'))
        
        clone = frag.cloneNode(0)
        deepClone = frag.cloneNode(1)

        assert not isSameNode(frag, clone), "Clone is same Node as original."
        assert not isSameNode(frag, deepClone), (
            "Clone is same Node as original.")

        checkAttribute(clone, 'parentNode', None)
        checkAttribute(deepClone, 'parentNode', None)
        checkLength(clone.childNodes, 0)
        checkLength(deepClone.childNodes, frag.childNodes.length)

        for i in range(deepClone.childNodes.length):
            checkAttribute(deepClone.childNodes.item(i), 'nodeType',
                frag.childNodes.item(i).nodeType)
            checkAttribute(deepClone.childNodes.item(i), 'data',
                frag.childNodes.item(i).data)


class DocumentFragmentWriteTestCase(NodeWriteTestCaseBase):

    def setUp(self):
        self.createDocument()
        self.docfrag = self.node = self.document.createDocumentFragment()

    def checkInsertBeforeEnd(self):
        doc = self.document
        fragment = self.docfrag
        fragment.appendChild(doc.createElement('foo'))
        fragment.appendChild(doc.createTextNode('textual magic'))
        docelem = doc.documentElement

        docelem.insertBefore(fragment, None)
        checkAttribute(docelem.childNodes[0], "nodeName", "foo")
        checkAttribute(docelem.childNodes[1], "nodeValue", "textual magic")
        checkLength(docelem.childNodes, 2)
        checkAttribute(docelem.firstChild, "nodeName", "foo")


# --- NodeList

class NodeListReadTestCase(TestCaseBase):

    def setUp(self):
        self.createDocument()
        self.list = self.document.createElement("foo")._get_childNodes()

    def checkGetLength(self):
        checkLength(self.list, 0)
        checkLength(self.document.childNodes, 1)

    def checkItem(self):
        assert self.list.item(0) is None

    def checkGetItem(self):
        try:
            self.list[0]
            assert 0, "expected IndexError to be raised"
        except IndexError:
            pass

    # there's no cmp for NodeList right now
    #def checkCmp(self):
    #    list1 = self.document.documentElement._get_childNodes()
    #    list2 = self.document.firstChild._get_childNodes()
    #    assert list1 == list2, "two NodeLists of the same thing don't compare"

##     def checkGetSlice(self):
##         assert self.list[2 : 5] == []       


02231 class NodeListWriteTestCase(TestCaseBase):

    def setUp(self):
        self.createDocument()
        self.list = self.document.childNodes

    def checkGetLength(self):
        checkLength(self.list, 1)
        self.document.appendChild(self.document.createComment('foo'))
        # A NodeList is 'live', changes to source Node should be reflected
        checkLength(self.list, 2)

    def checkItem(self):
        newNode = self.document.createComment('foo')
        self.document.appendChild(newNode)
        # A NodeList is 'live', changes to source Node should be reflected
        isSameNode(newNode, self.list.item(1))

        # This extends to the Nodes contained in the NodeList
        newNode.data = 'bar'
        checkAttribute(self.list.item(1), 'data', 'bar')


# --- NamedNodeMap

class NamedNodeMapReadTestCase(TestCaseBase):

    def setUp(self):
        self.map = self.createDocument().createElement("foo")._get_attributes()

    def checkGetLength(self):
        checkLength(self.map, 0)

    def checkGetNamedItem(self):
        assert self.map.getNamedItem("uuu") is None

    def checkRemoveNamedItem(self):
        try:
            self.map.removeNamedItem("uuu")
            assert 0, "expected NotFoundErr to be raised"
        except xml.dom.NotFoundErr:
            pass

    def checkItem(self):
        assert self.map.item(0) is None

    def checkGetItem(self):
        try:
            self.map["uuu"]
            assert 0, "expected KeyError to be raised"
        except KeyError:
            pass

    def checkGet(self):
        assert self.map.get("uuu", 5) == 5

    def checkHasKey(self):
        assert not self.map.has_key("uuu")

    def checkItems(self):
        assert self.map.items() == []

    def checkKeys(self):
        assert self.map.keys() == []

    def checkValues(self):
        assert self.map.values() == []    


class NonemptyNamedNodeMapWriteTestCase(TestCaseBase):

    def setUp(self):
        self.map = self.createDocument().createElement("foo")._get_attributes()
        self.attribute = self.document.createAttribute("attrName")
        self.attribute.value = "attrValue"
        self.map.setNamedItem(self.attribute)

    def checkRemoveNonexistentNamedItem(self):
        try:
            self.map.removeNamedItem("uuu")
            assert 0, "did not catch expected DOMException"
        except xml.dom.DOMException:
            pass

    def checkRemoveNamedItem(self):
        attribute2 = self.document.createAttribute("attrName2")
        self.map.setNamedItem(attribute2)        
        attrOut = self.map.removeNamedItem("attrName2")
        assert isSameNode(attrOut, attribute2)

    def checkRemoveNamedItemNotFound(self):
        try:
            self.map.removeNamedItem("bogus")
        except xml.dom.NotFoundErr:
            pass
        else:
            assert 0, (
                "Expected an exception when trying to remove non-existing "
                "entry.")

    def checkGetLength(self):
        checkLength(self.map, 1)

    def checkGetNamedItem(self):
        assert isSameNode(self.attribute, self.map.getNamedItem("attrName"))

    def checkGetNonexistentNamedItem(self):
        assert self.map.getNamedItem("uuu") is None

    def checkItem(self):
        assert isSameNode(self.map.item(0), self.attribute)
        assert isSameNode(self.attribute, self.map.item(0))

    def checkGetNonexistentItem(self):
        try:
            self.map["uuu"]
            assert 0, "expected KeyError to be raised"
        except KeyError:
            pass

    def checkGetItem(self):
        assert isSameNode(self.attribute, self.map["attrName"])

    def checkGet(self):
        assert isSameNode(self.attribute, self.map.get("attrName"))

    def checkHasKey(self):
        assert self.map.has_key("attrName")
        assert not self.map.has_key("uuu")

    def checkItems(self):
        key, node = self.map.items()[0]
        assert key == "attrName" and isSameNode(self.attribute, node)

    def checkKeys(self):
        assert self.map.keys() == ["attrName"]

    def checkValues(self):
        L = []
        for attr in self.map.values():
            L.append(attr.value)
        assert L == ["attrValue"], "bad values list: %s" % `L`

    def checkSetNamedItem(self):
        newAttr = self.document.createAttribute('someAttr')
        newAttr.value = 'spam'
        
        retVal = self.map.setNamedItem(newAttr)
        assert retVal is None, "setNamedItem returned %s" % repr(retVal)
        checkLength(self.map, 2)
        assert isSameNode(self.map.getNamedItem('someAttr'), newAttr), (
            "setNamedItem store seems to have failed, can't retrieve.")

    def checkSetNamedItemReplacingExistingNode(self):
        newAttr = self.document.createAttribute('someAttr')
        self.map.setNamedItem(newAttr)
        anotherAttr = self.document.createAttribute('someAttr')
        anotherAttr.value = 'eggs'

        retVal = self.map.setNamedItem(newAttr)
        assert retVal is not None, "setNamedItem returned None"
        assert isSameNode(retVal, newAttr), (
            "setNamedItem didn't return replaced Node.")
        checkLength(self.map, 2)

    def checkSetNamedItemWrongDocument(self):
        newDoc = self.implementation.createDocument(None, 'foo', None)
        foreignAttr = newDoc.createAttribute('someAttr')
        try:
            self.map.setNamedItem(foreignAttr)
        except xml.dom.WrongDocumentErr:
            pass
        else:
            assert 0, "Was allowed to add foreign Node to NamedNodeMap."

    def checkSetNamedItemAlreadyInUse(self):
        el = self.document.createElement('someElement')
        attr = self.document.createAttribute('someAttribute')
        el.setAttributeNode(attr)
        try:
           self.map.setNamedItem(attr)
        except xml.dom.InuseAttributeErr:
            pass
        else:
            assert 0, (
                "Was allowed to add attribute already in use to NamedNodeMap.")

    def checkSetNamedItemHierarchyRequestErr(self):
        # See DOM erratum core-4.
        textNode = self.document.createTextNode('text node')
        try:
            self.map.setNamedItem(textNode)
        except xml.dom.HierarchyRequestErr:
            pass
        else:
            assert 0, (
                "Was allowed to add a Node Type not belonging in this "
                "NamedNodeMap (a Text Node to a map of attributes).")


cases = buildCases(__name__, 'Core', None)

Generated by  Doxygen 1.6.0   Back to index