#! /usr/bin/env python

import unittest, logging

from sys import maxunicode
from tempfile import mkdtemp
import shutil, os.path
from StringIO import StringIO
from types import UnicodeType

MAX_CHAR=0x10FFFF
if maxunicode<MAX_CHAR:
	MAX_CHAR=maxunicode

def suite():
	return unittest.TestSuite((
		unittest.makeSuite(XML20081126Tests,'test'),
		unittest.makeSuite(XMLCharacterTests,'test'),
		unittest.makeSuite(XMLValidationTests,'test'),
		unittest.makeSuite(XMLEntityTests,'test'),
		unittest.makeSuite(XMLParserTests,'test'),		
		unittest.makeSuite(ElementTests,'test'),
		unittest.makeSuite(DocumentTests,'test')
		))

TEST_DATA_DIR=os.path.join(os.path.split(os.path.abspath(__file__))[0],'data_xml20081126')

from pyslet.xml20081126.structures import *
from pyslet.xml20081126.parser import XMLParser


class PIRecorderElement(Element):
	def __init__(self,parent):
		Element.__init__(self,parent)
		self.target=None
		self.instruction=None
		
	def ProcessingInstruction(self,target,instruction):
		self.target=target
		self.instruction=instruction

	
class NamedElement(Element):
	XMLNAME="test"
	XMLCONTENT=ElementType.ElementContent

def DecodeYN(value):
	return value=='Yes'
	
def EncodeYN(value):
	if value:
		return 'Yes'
	else:
		return 'No'


class GenericElementA(Element):
	pass

class GenericSubclassA(GenericElementA):
	pass

class GenericElementB(Element):
	pass

class GenericSubclassB(GenericElementB):
	pass


class ReflectiveElement(Element):
	XMLNAME="reflection"
	
	XMLATTR_btest='bTest'
	XMLATTR_ctest=('cTest',DecodeYN,EncodeYN)
	XMLATTR_dtest=('dTest',DecodeYN,EncodeYN,types.ListType)
	XMLATTR_dtestR=('dTestR',DecodeYN,EncodeYN) # legacy test
	XMLATTR_etest=('eTest',DecodeYN,EncodeYN,types.DictType)
	XMLATTR_etestR=('eTestR',DecodeYN,EncodeYN) 	# legacy test
	XMLATTR_ftest='fTest'		# missing attribute value
	
	def __init__(self,parent):
		Element.__init__(self,parent)
		self.atest=None
		self.bTest=None
		self.cTest=None
		self.dTest=None
		self.dTestR=[]
		self.eTest=None
		self.eTestR={}
		self.child=None
		self.generics=[]
		self.GenericElementB=None
		
	def GetChildren(self):
		for child in Element.GetChildren(self):
			yield child
		if self.child:
			yield self.child
		
	def ReflectiveElement(self):
		if self.child:
			return self.child
		else:
			e=ReflectiveElement(self)
			self.child=e
			return e

	def GenericElementA(self,childClass=GenericElementA):
		child=childClass(self)
		self.generics.append(child)
		return child
		
	
class ReflectiveDocument(Document):
	
	@classmethod
	def GetElementClass(cls,name):
		if name in ["reflection","etest"]:
			return ReflectiveElement
		else:
			return Element

		
class EmptyElement(Element):
	XMLNAME="empty"
	XMLCONTENT=XMLEmpty
	
class ElementContent(Element):
	XMLNAME="elements"
	XMLCONTENT=ElementContent

class MixedElement(Element):
	XMLNAME="mixed"
	XMLCONTENT=XMLMixedContent

class IDElement(Element):
	XMLName="ide"
	XMLCONTENT=XMLEmpty
	ID="id"

class BadElement:
	XMLNAME="bad"

	
class Elements:
	named=NamedElement
	reflective=ReflectiveElement
	empty=EmptyElement
	elements=ElementContent
	mixed=MixedElement
	id=IDElement
	bad=BadElement
	
class XML20081126Tests(unittest.TestCase):		
	def testCaseConstants(self):
		#self.assertTrue(APP_NAMESPACE=="http://www.w3.org/2007/app","Wrong APP namespace: %s"%APP_NAMESPACE)
		#self.assertTrue(ATOMSVC_MIMETYPE=="application/atomsvc+xml","Wrong APP service mime type: %s"%ATOMSVC_MIMETYPE)
		#self.assertTrue(ATOMCAT_MIMETYPE=="application/atomcat+xml","Wrong APP category mime type: %s"%ATOMCAT_MIMETYPE)
		pass

	def testCaseDeclare(self):
		classMap={}
		MapClassElements(classMap,Elements)
		self.assertTrue(type(classMap['mixed']) is types.TypeType,"class type not declared")
		self.assertFalse(hasattr(classMap,'bad'),"class type declared by mistake")

		
class XMLCharacterTests(unittest.TestCase):
	# Test IsNameChar
	def testChar(self):
		"""[2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]"""
		expectedEdges=[0x9,0xB,0xD,0xE,0x20,0xD800,0xE000,0xFFFE,0x10000,0x110000]
		if MAX_CHAR<0x10FFFF:
			expectedEdges=expectedEdges[0:8]
			logging.warn("xml tests truncated to unichr(0x%X) by narrow python build"%MAX_CHAR)
		self.assertTrue(self.FindEdges(IsChar,MAX_CHAR)==expectedEdges,"IsChar range test: "+str(self.FindEdges(IsChar,MAX_CHAR)))

	def testSpace(self):
		"""[3] S ::= (#x20 | #x9 | #xD | #xA)+"""
		expectedEdges=[0x9,0xB,0xD,0xE,0x20,0x21]
		self.assertTrue(self.FindEdges(IsS,256)==expectedEdges,"IsS")
	
	def testNameStart(self):
		"""[4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
		[5] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]"""
		nNameStartChars=0
		nNameChars=0
		for code in xrange(0x10000):
			c=unichr(code)
			if IsNameChar(c):
				nNameChars+=1
				if IsNameStartChar(c):
					nNameStartChars+=1
			else:
				self.assertFalse(IsNameStartChar(c),"NameStart not a name char: %s"%c)
		self.assertTrue(nNameChars==54129,"name char total %i"%nNameChars)
		self.assertTrue(nNameStartChars==54002,"name start char total %i"%nNameStartChars)
	
	def testPubidChar(self):
		"""[13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] """
		matchSet=" \x0d\x0a-'()+,./:=?;!*#@$_%abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
		for code in xrange(0xFF):
			c=unichr(code)
			if IsPubidChar(c):
				if not c in matchSet:
					self.fail("PubidChar false positive: %s"%c)
			else:
				if c in matchSet:
					self.fail("PubidChar not recognized: %s"%c)
					
	def testCharClasses(self):
		"""[84] Letter ::= BaseChar | Ideographic
		[85] BaseChar ::= [#x0041-#x005A] | ...
		[86] Ideographic ::= [#x4E00-#x9FA5] | #x3007 | [#x3021-#x3029]
		[87] CombiningChar ::= [#x0300-#x0345] | ...
		[88] Digit ::= [#x0030-#x0039] | ...
		[89] Extender ::= #x00B7 | ..."""
		nBaseChars=0
		nIdeographics=0
		nCombiningChars=0
		nDigits=0
		nExtenders=0
		for code in xrange(0x10000):
			c=unichr(code)
			if IsLetter(c):
				if IsIdeographic(c):
					nIdeographics+=1
				elif IsBaseChar(c):
					nBaseChars+=1
				else:
					self.fail("unichr(%#x) is a letter but not an ideographic or base character"%code)
			else:
				self.assertFalse(IsIdeographic(c) or IsBaseChar(c),
					"unichr(%#x) is an ideographic or base character but not a letter")
			if IsCombiningChar(c):
				nCombiningChars+=1
			if IsDigit(c):
				nDigits+=1
			if IsExtender(c):
				nExtenders+=1
		self.assertTrue(nBaseChars==13602,"base char total %i"%nBaseChars)
		self.assertTrue(nIdeographics==20912,"ideographic char total %i"%nIdeographics)
		self.assertTrue(nCombiningChars==437,"combing char total %i"%nCombiningChars)
		self.assertTrue(nDigits==149,"digit total %i"%nDigits)
		self.assertTrue(nExtenders==18,"extender total %i"%nExtenders)

	def FindEdges(self,testFunc,max):
		edges=[]
		flag=False
		for code in xrange(max+1):
			c=unichr(code)
			if flag!=testFunc(c):
				flag=not flag
				edges.append(code)
		if flag:
			edges.append(max+1)
		return edges

class XMLValidationTests(unittest.TestCase):
	def testCaseName(self):
		self.assertTrue(IsValidName("Simple"))
		self.assertTrue(IsValidName(":BadNCName"))
		self.assertTrue(IsValidName("prefix:BadNCName"))
		self.assertTrue(IsValidName("_GoodNCName"))
		self.assertFalse(IsValidName("-BadName"))
		self.assertFalse(IsValidName(".BadName"))
		self.assertFalse(IsValidName("0BadName"))
		self.assertTrue(IsValidName("GoodName-0.12"))
		self.assertFalse(IsValidName("BadName$"))
		self.assertFalse(IsValidName("BadName+"))
		self.assertTrue(IsValidName(u"Caf\xe9"))

	def testWellFormed(self):
		dPath=os.path.join(TEST_DATA_DIR,'wellformed')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f)
			d=Document()
			p=XMLParser(e)
			p.checkValidity=False
			try:
				p.ParseDocument(d)
				self.assertTrue(p.valid is None,"Well-Formed Example: %s marked valid but checkValidity was False"%fName)
			except XMLWellFormedError,e:
				self.fail("Well-Formed Example: %s raised XMLWellFormedError\n%s"%(fName,str(e)))
		dPath=os.path.join(TEST_DATA_DIR,'notwellformed')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f)
			d=Document()
			try:
				d.Read(e)
				self.fail("%s is not Well-Formed but failed to raise XMLWellFormedError"%fName)
			except XMLWellFormedError,e:
				logging.info("\n%s: Well-formed Errors:",fName)
				logging.info(str(e))
		
	def testValid(self):
		dPath=os.path.join(TEST_DATA_DIR,'valid')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f)
			p=XMLParser(e)
			p.checkValidity=True
			p.raiseValidityErrors=True
			try:
				p.ParseDocument()
				self.assertTrue(p.valid,"Valid Example: %s not marked as valid in the parser"%fName)
				self.assertTrue(len(p.nonFatalErrors)==0,"Valid Example: %s reported validity errors"%fName)
			except XMLValidityError,e:
				self.fail("Valid Example: %s raised XMLValidityError\n%s"%(fName,str(e)))
			except XMLWellFormedError,e:
				self.fail("Valid Example: %s raised XMLWellFormedError"%fName)
		dPath=os.path.join(TEST_DATA_DIR,'wellformed')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f)
			p=XMLParser(e)
			p.checkValidity=True
			# By default we don't raise validity errors...
			try:
				p.ParseDocument()
				self.assertFalse(p.valid,"Invalid Example: %s marked as valid in the parser"%fName)
				self.assertFalse(len(p.nonFatalErrors)==0,"Invalid Example: %s reported no validity errors"%fName)
				logging.info("\n%s: Validity Errors:",fName)
				for e in p.nonFatalErrors:
					logging.info(str(e))
			except XMLValidityError,e:
				self.fail("XMLValidityError raised when raiseVaidityErrors is False (%s)"%fName)
			except XMLWellFormedError,e:
				self.fail("Invalid but Well-Formed Example raised XMLWellFormedError (%s)\n%s"%(fName,str(e)))
			except XMLError,e:
				self.fail("Other XMLError raised by invalid but Well-Formed Example (%s)"%fName)


	def testIncompatible(self):
		dPath=os.path.join(TEST_DATA_DIR,'compatible')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f)
			p=XMLParser(e)
			p.checkCompatibility=True
			try:
				p.ParseDocument()
				self.assertTrue(len(p.nonFatalErrors)==0,"Compatible Example: %s reported compatibility errors"%fName)
			except XMLValidityError,e:
				self.fail("Compatible Example: %s raised XMLValidityError"%fName)
			except XMLWellFormedError,e:
				self.fail("Compatible Example: %s raised XMLWellFormedError"%fName)
			except XMLError,e:
				self.fail("Compatible Example: %s raised other XMLError"%fName)
		dPath=os.path.join(TEST_DATA_DIR,'incompatible')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f)
			p=XMLParser(e)
			p.checkCompatibility=True
			try:
				p.ParseDocument()
				self.assertFalse(len(p.nonFatalErrors)==0,"Incompatible Example: %s reported no non-fatal errors"%fName)
				logging.info("\n%s: Compatibility Errors:",fName)
				for e in p.nonFatalErrors:
					logging.info(str(e))
			except XMLValidityError,e:
				self.fail("XMLValidityError raised when raiseVaidityErrors is False (%s)"%fName)
			except XMLWellFormedError,e:
				self.fail("Incompatible but Well-Formed Example raised XMLWellFormedError (%s)"%fName)
			except XMLError,e:
				self.fail("Other XMLError raised by incompatible but Well-Formed Example (%s)"%fName)


	def testError(self):
		dPath=os.path.join(TEST_DATA_DIR,'noerrors')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f,'latin-1' if "latin" in fName else None)
			p=XMLParser(e)
			p.checkAllErrors=True
			try:
				p.ParseDocument()
				self.assertTrue(len(p.nonFatalErrors)==0,"No errors example: %s reported errors"%fName)
			except XMLError,e:
				self.fail("No errors example: %s raised XMLError"%fName)
		dPath=os.path.join(TEST_DATA_DIR,'errors')
		for fName in os.listdir(dPath):
			if fName[-4:]!=".xml":
				continue
			f=URIFactory.URLFromPathname(os.path.join(dPath,fName))
			e=XMLEntity(f,'latin-1' if "latin" in fName else None)
			p=XMLParser(e)
			p.checkAllErrors=True
			try:
				p.ParseDocument()
				self.assertFalse(len(p.nonFatalErrors)==0,"Error example: %s reported no non-fatal errors"%fName)
				logging.info("\n%s: Errors:",fName)
				for e in p.nonFatalErrors:
					logging.info(str(e))
			except XMLError,e:
				self.fail("XMLError raised by (non-fatal) error example (%s)"%fName)


class XMLEntityTests(unittest.TestCase):
	def testCaseConstructor(self):
		e=XMLEntity("<hello>")
		self.assertTrue(e.lineNum==1)
		self.assertTrue(e.linePos==1)
		self.assertTrue(type(e.theChar) is UnicodeType and e.theChar==u'<')
		e=XMLEntity(u"<hello>")
		self.assertTrue(type(e.theChar) is UnicodeType and e.theChar==u'<')
		e=XMLEntity(StringIO("<hello>"))
		self.assertTrue(e.lineNum==1)
		self.assertTrue(e.linePos==1)
		self.assertTrue(type(e.theChar) is UnicodeType and e.theChar==u'<')

	def testCaseChars(self):
		e=XMLEntity("<hello>")
		for c in "<hello>":
			self.assertTrue(e.theChar==c)
			e.NextChar()
		self.assertTrue(e.theChar is None)
		e.Reset()
		self.assertTrue(e.theChar=='<')

	def testLines(self):
		e=XMLEntity("Hello\nWorld\n!")
		while e.theChar is not None:
			c=e.theChar
			e.NextChar()
		self.assertTrue(e.lineNum==3)
		self.assertTrue(e.linePos==2)

	def testCodecs(self):
		m=u'Caf\xe9'
		e=XMLEntity('Caf\xc3\xa9')
		self.assertTrue(e.bom is False,'defaulted utf-8 BOM detection')
		for c in m:
			self.assertTrue(e.theChar==c,"Print: parsing utf-8 got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()
		e=XMLEntity('Caf\xe9','latin_1')
		self.assertTrue(e.bom is False,'latin_1 BOM detection')
		for c in m:
			self.assertTrue(e.theChar==c,"Print: parsing latin-1 got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()
		# This string should be automatically detected
		e=XMLEntity('\xff\xfeC\x00a\x00f\x00\xe9\x00')
		self.assertTrue(e.bom is True,'utf-16-le BOM detection')
		for c in m:
			self.assertTrue(e.theChar==c,"Print: parsing utf-16LE got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()		
		e=XMLEntity('\xfe\xff\x00C\x00a\x00f\x00\xe9')
		self.assertTrue(e.bom is True,'utf-16-be BOM detection')
		for c in m:
			self.assertTrue(e.theChar==c,"Print: parsing utf-16BE got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()			
		e=XMLEntity('\xef\xbb\xbfCaf\xc3\xa9','utf-8')
		self.assertTrue(e.bom is False,'utf-8 BOM detection')
		for c in m:
			self.assertTrue(e.theChar==c,"Print: parsing utf-8 with BOM got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()
		e=XMLEntity('Caf\xe9')
		for c in 'Ca':
			e.NextChar()
		e.ChangeEncoding('ISO-8859-1')
		self.assertTrue(e.theChar=='f',"Bad encoding change")
		e.NextChar()
		self.assertTrue(e.theChar==u'\xe9',"Print: change encoding got %s instead of %s"%(repr(e.theChar),repr(u'\xe9')))
		e=XMLEntity('C\x00a\x00f\x00\xe9\x00','utf-16-le')
		self.assertTrue(e.bom is False,'utf-16-le no BOM detection error')
		for c in m:
			self.assertTrue(e.theChar==c,"Print: parsing utf-16LE no BOM got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()		
		e=XMLEntity('\x00<\x00?\x00C\x00a\x00f\x00\xe9')		# add <? to trigger auto-detection
		self.assertTrue(e.bom is False,'utf-16-be no BOM detection error')
		for c in u"<?"+m:
			self.assertTrue(e.theChar==c,"Print: parsing utf-16BE no BOM got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()		
		e=XMLEntity('\xfe\xff\xfe\xff\x00C\x00a\x00f\x00\xe9')
		for c in u'\ufeff'+m:
			self.assertTrue(e.theChar==c,"Print: parsing double BOM got %s instead of %s"%(repr(e.theChar),repr(c)))
			e.NextChar()			
		
		
class XMLParserTests(unittest.TestCase):
	def testCaseConstructor(self):
		e=XMLEntity("<hello>")
		p=XMLParser(e)

	def testCaseRewind(self):
		data="Hello\r\nWorld\nCiao\rTutti!"
		data2="Hello\nWorld\nCiao\nTutti!"
		e=XMLEntity(data)
		p=XMLParser(e)
		for i in xrange(len(data2)):
			self.assertTrue(p.theChar==data2[i],"Failed at data[%i] before look ahead"%i)
			for j in xrange(5):
				data=[]
				for k in xrange(j):
					if p.theChar is not None:
						data.append(p.theChar)
					p.NextChar()
				p.BuffText(string.join(data,''))
				self.assertTrue(p.theChar==data2[i],"Failed at data[%i] after Rewind(%i)"%(i,j))
			p.NextChar()
	
	def testCaseNamecaseGeneral(self):
		data="Hello GoodBye"
		e=XMLEntity(data)
		p=XMLParser(e)
		self.assertFalse(p.ParseLiteral("HELLO"),"Case insensitve literal in default parser")
		p.sgmlNamecaseGeneral=True
		self.assertTrue(p.ParseLiteral("HELLO"),"Upper-case literals")
		p.ParseS()
		#self.assertTrue(p.ParseName()=="GOODBYE","Upper-case general names")

	def testDocument(self):
		"""[1] document ::= prolog element Misc* """
		os.chdir(TEST_DATA_DIR)
		f=open('readFile.xml','rb')
		e=XMLEntity(f)
		d=Document()
		d.Read(e)
		root=d.root
		self.assertTrue(isinstance(root,Element))
		self.assertTrue(root.xmlname=='tag' and root.GetValue()=='Hello World')
		f.close()
		f=open('readFile.xml','rb')
		e=XMLEntity(f)
		p=XMLParser(e)
		p.ParseDocument()
		root=p.doc.root
		self.assertTrue(isinstance(root,Element))
		self.assertTrue(root.xmlname=='tag' and root.GetValue()=='Hello World')
		f.close()
	
	# Following production is implemented as a character class:
	# [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
	
	def testCaseS(self):
		"""[3] S ::= (#x20 | #x9 | #xD | #xA)+ """
		e=XMLEntity(" \t\r\n \r \nH ello")
		p=XMLParser(e)
		self.assertTrue(p.ParseS()==" \t\n \n \n")
		self.assertTrue(p.theChar=='H')
		p.NextChar()
		try:
			p.ParseRequiredS()
		except XMLWellFormedError:
			self.fail("ParseRequiredS failed to parse white space")
		try:
			p.ParseRequiredS()
			self.fail("ParseRequiredS failed to throw exception")
		except XMLWellFormedError:
			pass
			
	# Following two productions are implemented as character classes:
	# [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
	# [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]

	def testCaseName(self):
		"""[5] Name ::= NameStartChar (NameChar)*"""
		sGood=('hello',':ello',u'A\xb72','_')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			name=p.ParseName()
			self.assertTrue(name==s,u"Name: %s (expected %s)"%(name,s))
		sBad=('-Atlantis','&hello','fish&chips','what?','.ello',u'\xb7RaisedDot','-')
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				name=p.ParseName()
				self.assertFalse(p.theChar is None,"ParseName negative test: %s"%s)
			except XMLWellFormedError:
				pass
		e=XMLEntity('&noname')
		p=XMLParser(e)
		try:
			p.ParseRequiredName()
			self.fail("ParseRequiredName: failed to throw exception")
		except XMLWellFormedError:
			pass
			
	def testCaseNames(self):
		"""[6] Names ::= Name (#x20 Name)*	"""
		e=XMLEntity("Hello World -Atlantis!")
		p=XMLParser(e)
		self.assertTrue(p.ParseNames()==['Hello','World'])
			
	def testCaseNmtoken(self):
		"""[7] Nmtoken ::= (NameChar)+"""
		sGood=('hello','h:ello','-Atlantis',':ello',u'\xb7RaisedDot',u'1\xb72','-')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			name=p.ParseNmtoken()
			self.assertTrue(name==s,u"Nmtoken: %s"%name)
		sBad=('&hello','fish&chips','what?')
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				name=p.ParseNmtoken()
				self.assertFalse(p.theChar is None,"ParseNmtoken negative test: %s"%s)
			except XMLWellFormedError:
				pass		

	def testCaseNmtokens(self):
		"""[8] Nmtokens ::= Nmtoken (#x20 Nmtoken)*"""
		e=XMLEntity("Hello World -Atlantis!")
		p=XMLParser(e)
		tokens=p.ParseNmtokens()
		self.assertTrue(tokens==['Hello','World','-Atlantis'],repr(tokens))

	def testCaseEntityValue(self):
		"""[9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | "'" ([^%&'] | PEReference | Reference)* "'"	"""
		e=XMLEntity("'first'\"second\"'3&gt;2''2%ltpe;3'")
		m=['first','second','3&gt;2','2<3']
		p=XMLParser(e)
		p.checkValidity=True
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('ltpe','<'))
		for match in m:
			value=p.ParseEntityValue()
			self.assertTrue(value==match,"Match failed: %s (expected %s)"%(value,match))

	def testAttValue(self):
		"""[10] AttValue ::= '"' ([^<&"] | Reference)* '"' |  "'" ([^<&'] | Reference)* "'" """
		e=XMLEntity("'first'\"second\"'3&gt;2''Caf&#xE9;'")
		m=['first','second','3>2',u'Caf\xe9']
		p=XMLParser(e)
		for match in m:
			value=p.ParseAttValue()
			self.assertTrue(value==match,"Match failed: %s (expected %s)"%(value,match))
		sBad=('"3<2"',"'Fish&Chips'")
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				value=p.ParseAttValue()
				self.fail("AttValue negative test: %s"%s)
			except XMLWellFormedError:
				pass		
	
	def testSystemLiteral(self):
		"""[11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'") """
		e=XMLEntity("'first'\"second\"'3&gt;2''2%ltpe;3''Caf&#xE9;'")
		m=[u'first',u'second',u'3&gt;2',u'2%ltpe;3',u'Caf&#xE9;']
		p=XMLParser(e)
		for match in m:
			value=p.ParseSystemLiteral()
			self.assertTrue(value==match,"Match failed: %s (expected %s)"%(value,match))
	
	def testPubidLiteral(self):
		"""[12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"	"""
		e=XMLEntity("'first'\"second\"'http://www.example.com/schema.dtd?strict''[bad]'")
		m=['first','second','http://www.example.com/schema.dtd?strict']
		p=XMLParser(e)
		for match in m:
			value=p.ParsePubidLiteral()
			self.assertTrue(value==match,"Match failed: %s (expected %s)"%(value,match))
		try:
			value=p.ParsePubidLiteral()
			self.fail("Parsed bad PubidLiterasl: %s"%value)
		except XMLFatalError:
			pass

	# [13] PubidChar: tested as a character class
	
	def testCaseCharData(self):
		"""[14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*) """
		e=XMLEntity("First<Second&Third]]&Fourth]]>")
		m=['First','Second','Third]]','Fourth']
		p=XMLParser(e)
		p.doc=Document()
		for match in m:
			p.element=Element(p.doc)
			p.ParseCharData()
			p.NextChar()
			self.assertTrue(p.element.GetValue()==match,"Match failed: %s (expected %s)"%(p.element.GetValue(),match))

	def testCaseComment(self):
		"""[15] Comment ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->' """
		e=XMLEntity("<!--First--><!--Secon-d--><!--Thi<&r]]>d--><!--Fourt<!-h--><!--Bad-Comment--->")
		m=['First','Secon-d','Thi<&r]]>d','Fourt<!-h']
		p=XMLParser(e)
		for match in m:
			pStr=p.ParseComment()
			self.assertTrue(pStr==match,"Match failed: %s (expected %s)"%(pStr,match))
		try:
			if p.ParseLiteral('<!--'):
				pStr=p.ParseComment()
			self.fail("Parsed bad comment: %s"%pStr)
		except XMLFatalError:
			pass
		
	def testCasePI(self):
		"""[16] PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>' """
		e=XMLEntity("<?target instruction?><?xm_xml \n\r<!--no comment-->?><?markup \t]]>?&<?>")
		m=[('target','instruction'),('xm_xml','<!--no comment-->'),('markup',']]>?&<')]
		p=XMLParser(e)
		p.doc=Document()
		for matchTarget,matchStr in m:
			p.element=PIRecorderElement(p.doc)
			p.ParsePI()
			self.assertTrue(p.element.target==matchTarget,"Match failed for target: %s (expected %s)"%(p.element.target,matchTarget))
			self.assertTrue(p.element.instruction==matchStr,"Match failed for instruction: %s (expected %s)"%(p.element.instruction,matchStr))
		sBad=('<?xml reserved?>')
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.doc=Document()
			try:
				p.ParsePI()
				self.fail("PI negative test: %s"%s)
			except XMLWellFormedError:
				pass		

	def testPITarget(self):
		"""[17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))	"""
		sGood=('hello','helloxml','xmlhello','xmhello','xm','ml','xl')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			name=p.ParsePITarget()
			self.assertTrue(name==s,"PITarget: %s"%name)
		sBad=('xml','XML','xML','Xml')
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				name=p.ParsePITarget()
				self.fail("PITarget negative test: %s"%s)
			except XMLWellFormedError:
				pass		
		
	def testCDSect(self):
		"""[18] CDSect ::= CDStart CData CDEnd	"""
		sGood=('<![CDATA[hello]]>',
			"<![CDATA[]]>",
			"<![CDATA[a]b]]c]>d><![CDATAe]]>",
			'hello]]>',
			"<![CDATA[<hello>&world;]]>")
		m=['hello','','a]b]]c]>d><![CDATAe','hello',"<hello>&world;"]
		for s,match in zip(sGood,m):
			e=XMLEntity(s)
			p=XMLParser(e)
			p.doc=Document()
			p.element=Element(p.doc)
			p.ParseCDSect(p.theChar!='<')
			self.assertTrue(p.element.GetValue()==match,"CDSect conent: %s"%p.element.GetValue())
		sBad=('<!CDATA [hello]]>',
			"<!CDATA[hello]]",
			"hello")
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.doc=Document()
			p.element=Element(p.doc)
			try:
				p.ParseCDSect(p.theChar!='<')
				self.fail("CDSect negative test: %s"%s)
			except XMLWellFormedError:
				pass		
		e=XMLEntity("&hello;<end")
		p=XMLParser(e)
		p.doc=Document()
		p.element=Element(p.doc)
		p.ParseCDSect(True,'<end')
		self.assertTrue(p.element.GetValue()=='&hello;',"Custom CDSect: %s"%p.element.GetValue())
		
	def testCDStart(self):
		"""[21] CDStart ::= '<!CDATA['	"""
		e=XMLEntity("<![CDATA[")
		p=XMLParser(e)
		p.ParseCDStart()
		self.assertTrue(p.theChar is None,"Short parse on CDStart")
		
	def testCData(self):
		"""[20] CData ::= (Char* - (Char* ']]>' Char*))	"""
		sGood=('',' ','<!-- comment -->',
			'&hello;]]>',
			']',
			']]',
			']]h>')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.doc=Document()
			p.element=Element(p.doc)
			p.ParseCData()
			if p.theChar is None:
				self.assertTrue(p.element.GetValue()==s,"CData conent: %s"%p.element.GetValue())
			else:
				p.ParseCDEnd()
				self.assertTrue(p.element.GetValue()==s[:-3],"CData conent: %s"%p.element.GetValue())				
		# no negative tests as prolog can be empty, but check the custom CDEnd case.
		e=XMLEntity("hello<end")
		p=XMLParser(e)
		p.doc=Document()
		p.element=Element(p.doc)
		p.ParseCData('<end')
		self.assertTrue(p.element.GetValue()=='hello',"Custom CDEnd: %s"%p.element.GetValue())
		
	def testCDEnd(self):
		"""[21] CDEnd ::= ']]>'	"""
		e=XMLEntity("]]>")
		p=XMLParser(e)
		p.ParseCDEnd()
		self.assertTrue(p.theChar is None,"Short parse on CDEnd")
		
	def testProlog(self):
		"""[22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?	"""
		sGood=('',' ','<!-- comment -->',
			'<?xml version="1.0"?>',
			'<?xml version="1.0"?><!-- comment --> ',
			'<?xml version="1.0"?><!-- comment --> <!DOCTYPE steve>',
			'<?xml version="1.0"?><!-- comment --> <!DOCTYPE steve><?pi?> ')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.doc=Document()
			p.ParseProlog()
			self.assertTrue(p.theChar is None,"Short parse on Prolog")
		# no negative tests as prolog can be empty!
		
	def testXMLDecl(self):
		"""[23] XMLDecl ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'	"""
		sGood=('<?xml version="1.0" encoding="utf-8" standalone="no" ?>',
			"<?xml version='1.0' standalone='yes'?>",
			"<?xml version='1.0' encoding='utf-8'?>",
			"<?xml version='1.1'?>",
			" version='1.2'?>")
		m=[('1.0','utf-8',False),
			('1.0',None,True),
			('1.0','utf-8',False),
			('1.1',None,False),
			('1.2',None,False)]
		for s,match in zip(sGood,m):
			e=XMLEntity(s)
			p=XMLParser(e)
			d=p.ParseXMLDecl(not ('x' in s))
			self.assertTrue(isinstance(d,XMLDeclaration),"xml declaration type")
			self.assertTrue(d.version==match[0],"declared version mismatch: %s"%d.version)
			self.assertTrue(d.encoding==match[1],"declared encoding mismatch: %s"%d.encoding)
			self.assertTrue(d.standalone==match[2],"standalone declaration mismatch: %s"%d.standalone)
			self.assertTrue(p.theChar is None,"Short parse on XMLDecl")
		sBad=('','version="1.0"'," ='1.0'"," version=1.0")
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseXMLDecl()
				self.fail("XMLDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass		
		
	def testVersionInfo(self):
		"""[24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')	"""
		sGood=(' version="1.0"',"  version  =  '1.1'"," = '1.0'")
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.ParseVersionInfo(not ('v' in s))
			self.assertTrue(p.theChar is None,"Short parse on VersionInfo")
		sBad=('','version="1.0"'," ='1.0'"," version=1.0")
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseVersionInfo()
				self.fail("VersionInfo negative test: %s"%s)
			except XMLWellFormedError:
				pass		
		
	def testEq(self):
		"""[25] Eq ::= S? '=' S?	"""
		sGood=('=',' = ',' =','= ')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.ParseEq()
			self.assertTrue(p.theChar is None,"Short parse on Eq")
		sBad=('','-')
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseEq()
				self.fail("Eq negative test: %s"%s)
			except XMLWellFormedError:
				pass		
		
	def testVersionNum(self):
		"""[26] VersionNum ::= '1.' [0-9]+ """
		sGood=('1.0','1.10','1.1','1.0123456789')
		for s in sGood:
			e=XMLEntity(s)
			p=XMLParser(e)
			self.assertTrue(p.ParseVersionNum()==s,"Failed to parse VersionNum: %s"%s)
			self.assertTrue(p.theChar is None,"Short parse on VersionNum")
		sBad=('1. ','2.0','1','1,0')
		for s in sBad:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseVersionNum()
				self.fail("VersionNum negative test: %s"%s)
			except XMLWellFormedError:
				pass		
		
	def testMisc(self):
		"""[27] Misc ::= Comment | PI | S """
		s="<!-- comment --><?pi?> "
		e=XMLEntity(s)
		p=XMLParser(e)
		for i in xrange(3):
			p.ParseMisc()
		self.assertTrue(p.theChar is None,"Short parse of Misc")
		
	def testDoctypedecl(self):
		"""[28] doctypedecl ::= '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>'	"""
		s=["<!DOCTYPE Steve SYSTEM 'SteveDoc.dtd'[ <!ENTITY name 'Steve'> ]>",
			"<!DOCTYPE Steve SYSTEM 'SteveDoc.dtd' [] >",
			"<!DOCTYPE Steve SYSTEM 'SteveDoc.dtd' >",
			"<!DOCTYPE Steve [ ] >",
			"<!DOCTYPE Steve>"]
		m=[('Steve','SteveDoc.dtd','Steve'),
			('Steve','SteveDoc.dtd',None),
			('Steve','SteveDoc.dtd',None),
			('Steve',None,None),
			('Steve',None,None)]
		dtdPath=os.path.join(TEST_DATA_DIR,'SteveDoc.dtd')
		f=URIFactory.URLFromPathname(dtdPath)
		for sEntity,match in zip(s,m):
			e=XMLEntity(sEntity)
			e.location=f
			p=XMLParser(e)
			p.ParseDoctypedecl()
			self.assertTrue(isinstance(p.dtd,XMLDTD),"No DTD created")
			self.assertTrue(p.dtd.name==match[0],"Name mismatch")
			if match[1] is None:
				self.assertTrue(p.dtd.externalID is None,"External ID: expected None")
			else:
				self.assertTrue(isinstance(p.dtd.externalID,XMLExternalID),"Type of ExternalID")
				self.assertTrue(p.dtd.externalID.system==match[1],"System ID mismatch")
			if match[2] is not None:
				self.assertTrue(p.dtd.GetEntity('name').definition==match[2],"Expected general entity declared: %s"%repr(p.dtd.GetEntity('name').definition))
			
	def testDeclSep(self):
		"""[28a] DeclSep ::= PEReference | S"""
		s="%stuff; %stuff; x"
		e=XMLEntity(s)
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('stuff',' '))
		p.checkValidity=True
		p.refMode=XMLParser.RefModeInDTD
		while p.theChar!='x':
			p.ParseDeclSep()
		
	def testIntSubset(self):
		"""[28b] intSubset ::= (markupdecl | DeclSep)* """
		s="""<!ELEMENT elem1 ANY>
		<!ATTLIST elem1 attr CDATA 'Steve'>
		<!ENTITY name 'Steve'>
		<!NOTATION SteveN PUBLIC 'Steve' '/home/steve.txt'> 
		<?stuff?>
		<!-- more stuff -->
		x"""
		e=XMLEntity(s)
		p=XMLParser(e)
		p.doc=Document()
		p.ParseIntSubset()
		self.assertTrue(p.theChar=='x',"Short parse on internal subset: found %s"%repr(p.theChar))		
		
	def testMarkupDecl(self):
		"""[29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment	"""
		s="""<!ELEMENT elem1 ANY>
		<!ATTLIST elem1 attr CDATA 'Steve'>
		<!ENTITY name 'Steve'>
		<!NOTATION SteveN PUBLIC 'Steve' '/home/steve.txt'> 
		<?stuff?>
		<!-- more stuff -->
		x"""
		e=XMLEntity(s)
		p=XMLParser(e)
		p.checkValidity=True	# ensures that elements are declared in the DTD
		p.dtd=XMLDTD()
		while p.theChar=='<':
			p.ParseMarkupDecl(False)
			p.ParseS()
		self.assertTrue(p.theChar=='x',"Short parse on markup declarations: found %s"%repr(p.theChar))
		eType=p.dtd.GetElementType('elem1')
		self.assertTrue(eType.contentType==ElementType.Any,"element content type")
		aList=p.dtd.GetAttributeList('elem1')
		self.assertTrue(aList['attr'].defaultValue=='Steve',"attlist")
		self.assertTrue(p.dtd.GetEntity('name').definition=='Steve',"entity declaration")
		self.assertTrue(p.dtd.GetNotation('SteveN').externalID.system=='/home/steve.txt',"notation declaration")
		
	def testExtSubset(self):
		"""[30] extSubset ::= TextDecl? extSubsetDecl """
		s='<?xml encoding = "latin-1"?> <?stuff?> !'
		e=XMLEntity(s)
		p=XMLParser(e)
		p.ParseExtSubset()
		self.assertTrue(p.theChar=='!',"Short parse on extSubset: %s"%p.theChar)		
		s='<?stuff?> !'
		e=XMLEntity(s)
		p=XMLParser(e)
		p.ParseExtSubset()
		self.assertTrue(p.theChar=='!',"Short parse on extSubset: %s"%p.theChar)		
		
	def testExtSubsetDecl(self):
		"""[31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*	"""
		s="<?stuff?><![INCLUDE[]]>%moreStuff; "
		e=XMLEntity(s)
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('moreStuff',' <?stuff?>'))
		p.checkValidity=True
		p.refMode=XMLParser.RefModeInDTD
		p.ParseExtSubsetDecl()
		self.assertTrue(p.theChar is None,"Short parse on extSubsetDecl: %s"%p.theChar)		

	def testSDDecl(self):
		"""[32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"')) """
		e=XMLEntity(" standalone='yes' standalone = \"no\" standalone = 'bad'")
		m=[True,False]
		p=XMLParser(e)
		for match in m:
			value=p.ParseSDDecl()
			self.assertTrue(value==match,"Match failed: %s (expected %s)"%(value,match))
		try:
			value=p.ParseSDDecl()
			self.fail("Parsed bad SDDecl: %s"%value)
		except XMLFatalError:
			pass		
		
	# There are no productions [33]-[38]

	def testElement(self):
		"""[39] element ::= EmptyElemTag | STag content ETag """
		s="""<elem1/><elem2>hello</elem2><elem3>goodbye</elem4>"""
		e=XMLEntity(s)
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInContent
		element=p.element=Element("a")
		p.ParseElement()
		p.ParseElement()
		try:
			p.ParseElement()
			self.fail("Parsed bad element.")
		except XMLWellFormedError:
			pass
		children=list(element.GetChildren())
		self.assertTrue(isinstance(children[0],Element),"First element: %s"%repr(children[0]))
		self.assertTrue(children[0].xmlname=='elem1',"First element name: %s"%repr(children[0].xmlname))
		self.assertTrue(children[0].GetValue()=='',"First element empty value: %s"%repr(children[0].GetValue()))
		self.assertTrue(isinstance(children[1],Element),"Second element: %s"%repr(children[1]))
		self.assertTrue(children[1].xmlname=='elem2',"Second element name: %s"%repr(children[1].xmlname))
		self.assertTrue(children[1].GetValue()=='hello',"Second element value: %s"%repr(children[1].GetValue()))

		
	def testSTag(self):
		"""[40] STag ::= '<' Name (S Attribute)* S? '>' """
		e=XMLEntity("<tag hello='world' ciao=\"tutti\">")
		p=XMLParser(e)
		name,attrs,empty=p.ParseSTag()
		self.assertTrue(name=='tag' and attrs['hello']=='world' and attrs['ciao']=='tutti' and empty==False)
		e=XMLEntity("<tag hello/>")
		p=XMLParser(e)
		p.sgmlShorttag=True
		name,attrs,empty=p.ParseSTag()
		self.assertTrue(name=='tag' and attrs['@hello']=='hello' and empty is True)
		e=XMLEntity("<tag width=20%>")
		p=XMLParser(e)
		p.dontCheckWellFormedness=True
		name,attrs,empty=p.ParseSTag()
		self.assertTrue(name=='tag' and attrs['width']=='20%' and empty is False)
		
		
	def testAttribute(self):
		"""[41] Attribute ::= Name Eq AttValue """
		s="a='b'c=\"d\"e=f i j g=h%"
		e=XMLEntity(s)
		m=[('a','b'),('c','d')]
		p=XMLParser(e)
		for match in m:
			name,value=p.ParseAttribute()
			self.assertTrue(name==match[0],"Attribute name match failed: %s (expected %s)"%(name,match[0]))
			self.assertTrue(value==match[1],"Attribute value match failed: %s (expected %s)"%(value,match[1]))
		try:
			p.ParseS()
			value=p.ParseAttribute()
			self.fail("Parsed bad Attribute: %s"%value)
		except XMLWellFormedError:
			pass
		e=XMLEntity(s)
		m=[('a','b'),('c','d'),('e','f'),('@i','i'),('@j','j'),('g','h%')]
		p=XMLParser(e)
		p.dontCheckWellFormedness=True
		p.sgmlShorttag=True
		for match in m:
			p.ParseS()
			name,value=p.ParseAttribute()
			self.assertTrue(name==match[0],"Compatibility: Attribute name match failed: %s (expected %s)"%(name,match[0]))
			self.assertTrue(value==match[1],"Compatibility: Attribute value match failed: %s (expected %s)"%(value,match[1]))
		self.assertTrue(p.theChar is None,"Short parse of ETag tests")
		
	def testETag(self):
		"""[42] ETag ::= '</' Name S? '>' """
		s="</elem1>elem2></elem3/>"
		e=XMLEntity(s)
		m=['elem1','elem2']
		p=XMLParser(e)
		for match in m:
			value=p.ParseETag(p.theChar!='<')
			self.assertTrue(value==match,"ETag name match failed: %s (expected %s)"%(value,match))
		try:
			value=p.ParseETag()
			self.fail("Parsed bad ETag: %s"%value)
		except XMLWellFormedError:
			p.ParseLiteral('/>')
			pass
		self.assertTrue(p.theChar is None,"Short parse of ETag tests")
						
	def testContent(self):
		"""[43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)* """
		s="""a<elem1/>b&amp;c<![CDATA[&amp;]]>d<?x?>e<!-- y -->f"""
		e=XMLEntity(s)
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInContent
		p.element=Element("a")
		p.ParseContent()
		children=list(p.element.GetChildren())
		self.assertTrue(children[0]=='a',"First character: %s"%repr(children[0]))
		self.assertTrue(isinstance(children[1],Element),"First element: %s"%repr(children[1]))
		self.assertTrue(children[1].xmlname=='elem1',"First element name: %s"%repr(children[1].xmlname))
		self.assertTrue(children[2]=='b&c&amp;def',"Remaining data: %s"%repr(children[2]))

	def testEmptyElemTag(self):
		"""[44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/> """
		s="""<elem1/> <elem2 /> <elem3 x="1"/>"""
		e=XMLEntity(s)
		p=XMLParser(e)
		m=[('elem1',{}),('elem2',{}),('elem3',{'x':'1'})]
		for match in m:
			try:
				p.ParseEmptyElemTag()
				self.fail("Expected ParseEmptyElem to be unimplemented.")
			except NotImplementedError:
				pass
			name,attrs,emptyFlag=p.ParseSTag()
			self.assertTrue(emptyFlag,"Expected empty element tag")
			self.assertTrue(name==match[0],"Element name mismatch: %s"%name)
			self.assertTrue(attrs==match[1],"Element attrs mismatch: %s"%repr(attrs))
			p.ParseS()
		self.assertTrue(p.theChar is None,"Short parse of empty elements")
	
	def testElementDecl(self):
		"""[45] elementdecl ::= '<!ELEMENT' S Name S contentspec S? '>'	"""
		s="""<!ELEMENT elem1 ANY>
		<!ELEMENT elem2 (#PCDATA)>
		<!ELEMENT elem3 ( A | ( B,C )? | (D,E,F)* | (G,H)+ | (I | (J,K)+)* ) >"""
		e=XMLEntity(s)
		p=XMLParser(e)
		p.checkValidity=True	# ensures that elements are declared in the DTD
		p.dtd=XMLDTD()
		try:
			while True:
				p.ParseElementDecl(p.theChar!='<')
				p.ParseS()
		except XMLWellFormedError:
			pass
		eType=p.dtd.GetElementType('elem1')
		self.assertTrue(eType.contentType==ElementType.Any,"First element")
		eType=p.dtd.GetElementType('elem2')
		self.assertTrue(eType.contentType==ElementType.Mixed,"Second element")
		eType=p.dtd.GetElementType('elem3')
		self.assertTrue(eType.contentType==ElementType.ElementContent,"Third element")
		self.assertTrue(eType.contentModel.children[4].children[1].children[1].name=="K","Third element model")
		self.assertTrue(p.theChar is None,"Short parse on element declarations")
		
	def testContentSpec(self):
		"""[46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children """
		s="""EMPTY
			ANY
			(#PCDATA)
			(#PCDATA)*
			( #PCDATA | Steve1 | Steve2 )*
			(Particle2|Particle3)?
			(Particle4,Particle5,Particle6)+"""
		e=XMLEntity(s)
		m=[	(ElementType.Empty,None,None,None),
			(ElementType.Any,None,None,None),
			(ElementType.Mixed,XMLChoiceList,XMLContentParticle.ZeroOrMore,0),
			(ElementType.Mixed,XMLChoiceList,XMLContentParticle.ZeroOrMore,0),
			(ElementType.Mixed,XMLChoiceList,XMLContentParticle.ZeroOrMore,2),
			(ElementType.ElementContent,XMLChoiceList,XMLContentParticle.ZeroOrOne,2),
			(ElementType.ElementContent,XMLSequenceList,XMLContentParticle.OneOrMore,3) ]
		p=XMLParser(e)
		for match in m:
			eType=ElementType()
			p.ParseContentSpec(eType)
			self.assertTrue(eType.contentType==match[0],"Content type mismatch")
			if match[1] is None:
				self.assertTrue(eType.contentModel is None,"Content model type mismatch")
			else:	
				self.assertTrue(isinstance(eType.contentModel,match[1]),"Content model type mismatch")
				self.assertTrue(eType.contentModel.occurrence==match[2],"Content model occurrence mismatch")
				self.assertTrue(len(eType.contentModel.children)==match[3],"Number of children in content model mismatch")
			p.ParseS()
		self.assertTrue(p.theChar is None,"Incomplete parse in contentspec tests: %s"%repr(p.theChar))
		
	def testChildren(self):
		"""[47] children ::= (choice | seq) ('?' | '*' | '+')? """
		s="""(Particle2|Particle3)?
			(Particle4,Particle5)*
			(Particle6,(Particle7|Particle8),(Particle9,Particle10))+
			Particle1"""
		e=XMLEntity(s)
		m=[	(XMLChoiceList,XMLContentParticle.ZeroOrOne),
			(XMLSequenceList,XMLContentParticle.ZeroOrMore),
			(XMLSequenceList,XMLContentParticle.OneOrMore) ]
		p=XMLParser(e)
		for match in m:
			cp=p.ParseChildren()
			self.assertTrue(isinstance(cp,match[0]),"Particle type mismatch")
			self.assertTrue(cp.occurrence==match[1],"Particle occurrence mismatch, %i (expected %i)"%(cp.occurrence,match[1]))
			p.ParseS()
		try:
			cp=p.ParseChildren()
			self.fail("Name not allowed outside choice or sequence")
		except XMLFatalError:
			# fails to parse 'Particle1'
			p.ParseLiteral('Particle1')
		self.assertTrue(p.theChar is None,"Incomplete parse in children tests: %s"%repr(p.theChar))
		
	def testCP(self):
		"""[48] cp ::= (Name | choice | seq) ('?' | '*' | '+')? """
		s="""Particle1
			(Particle2|Particle3)?
			(Particle4,Particle5)*
			(Particle6,(Particle7|Particle8),(Particle9,Particle10))+"""
		e=XMLEntity(s)
		m=[	(XMLNameParticle,XMLContentParticle.ExactlyOnce),
			(XMLChoiceList,XMLContentParticle.ZeroOrOne),
			(XMLSequenceList,XMLContentParticle.ZeroOrMore),
			(XMLSequenceList,XMLContentParticle.OneOrMore) ]
		p=XMLParser(e)
		for match in m:
			cp=p.ParseCP()
			self.assertTrue(isinstance(cp,match[0]),"Particle type mismatch")
			self.assertTrue(cp.occurrence==match[1],"Particle occurrence mismatch, %i (expected %i)"%(cp.occurrence,match[1]))
			p.ParseS()
		self.assertTrue(p.theChar is None,"Incomplete parse in CP tests: %s"%repr(p.theChar))

	def testChoice(self):
		"""[49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')' """
		s="(Particle1|Particle2?|Particle3*)( Particle4+ | Particle5 )(Particle6|Particle7+)+(Particle8*)()"
		e=XMLEntity(s)
		m=[[('Particle1',XMLContentParticle.ExactlyOnce),('Particle2',XMLContentParticle.ZeroOrOne),
			('Particle3',XMLContentParticle.ZeroOrMore)],[('Particle4',XMLContentParticle.OneOrMore),
			('Particle5',XMLContentParticle.ExactlyOnce)],[('Particle6',XMLContentParticle.ExactlyOnce),
			('Particle7',XMLContentParticle.OneOrMore)]]
		p=XMLParser(e)
		for match in m:
			cp=p.ParseChoice()
			self.assertTrue(isinstance(cp,XMLChoiceList),"Choice list match failed")
			self.assertTrue(len(cp.children)==len(match),"Choice list match length mismatch")
			i=0
			for cpi,mi in zip(cp.children,match):
				self.assertTrue(isinstance(cpi,XMLNameParticle),"Not a name particle")
				self.assertTrue(cpi.name==mi[0],"Particle name mismatch")
				self.assertTrue(cpi.occurrence==mi[1],"Particle occurrence mismatch")
		self.assertTrue(p.ParseLiteral('+'),"Final occurrence parsed in error")
		try:
			cp=p.ParseChoice()
			self.fail("Singleton choice not allowed")
		except XMLFatalError:
			# fails to parse ')'
			p.ParseLiteral(')')
		try:
			cp=p.ParseChoice()
			self.fail("Empty choice not allowed")
		except XMLFatalError:
			# fails to parse ')'
			p.ParseLiteral(')')
		self.assertTrue(p.theChar is None,"Incomplete parse in choice tests: %s"%repr(p.theChar))
	
	def testSeq(self):
		"""[50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')' """
		s="(Particle1,Particle2?,Particle3*)( Particle4+ , Particle5 )(Particle6+)+()"
		e=XMLEntity(s)
		m=[[('Particle1',XMLContentParticle.ExactlyOnce),('Particle2',XMLContentParticle.ZeroOrOne),
			('Particle3',XMLContentParticle.ZeroOrMore)],[('Particle4',XMLContentParticle.OneOrMore),
			('Particle5',XMLContentParticle.ExactlyOnce)],[('Particle6',XMLContentParticle.OneOrMore)]]
		p=XMLParser(e)
		for match in m:
			cp=p.ParseSeq()
			self.assertTrue(isinstance(cp,XMLSequenceList),"Sequence match failed")
			self.assertTrue(len(cp.children)==len(match),"Sequence match length mismatch")
			i=0
			for cpi,mi in zip(cp.children,match):
				self.assertTrue(isinstance(cpi,XMLNameParticle),"Not a name particle")
				self.assertTrue(cpi.name==mi[0],"Particle name mismatch")
				self.assertTrue(cpi.occurrence==mi[1],"Particle occurrence mismatch")
		self.assertTrue(p.ParseLiteral('+'),"Final occurrence parsed in error")
		try:
			cp=p.ParseSeq()
			self.fail("Empty sequence not allowed")
		except XMLFatalError:
			# fails to parse ')'
			p.ParseLiteral(')')
		self.assertTrue(p.theChar is None,"Incomplete parse in sequence tests: %s"%repr(p.theChar))
		
	def testMixed(self):
		"""[51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')' """
		s="(#PCDATA)(#PCDATA)*( #PCDATA | Steve1 | Steve2 )*( #PCDATA |Steve1|Steve2)*(#PCDATA|Steve1)(Steve1|#PCDATA)*"
		e=XMLEntity(s)
		m=[[],[],['Steve1','Steve2'],['Steve1','Steve2']]
		p=XMLParser(e)
		for match in m:
			cp=p.ParseMixed()
			self.assertTrue(isinstance(cp,XMLChoiceList),"Mixed must be a choice")
			self.assertTrue(cp.occurrence==XMLContentParticle.ZeroOrMore,"Mixed must be '*'")
			self.assertTrue(len(cp.children)==len(match),"Particle count mismatch: %s"%str(match))
			for cpi,mi in zip(cp.children,match):
				self.assertTrue(isinstance(cpi,XMLNameParticle),"Mixed particles must be names")
				self.assertTrue(cpi.occurrence==XMLContentParticle.ExactlyOnce,"Mixed occurrence")
				self.assertTrue(cpi.name==mi,"Mixed particle name")
		try:
			values=p.ParseMixed()
			self.fail("Missed trailing *")
		except XMLFatalError:
			# fails to parse ')*'
			p.ParseLiteral(')')
		try:
			values=p.ParseMixed()
			self.fail("#PCDATA must come first")
		except XMLFatalError:
			# fails to parse '#PCDATA' 
			p.ParseLiteral('Steve1|#PCDATA)*')
		self.assertTrue(p.theChar is None,"Incomplete parse in Mixed tests: %s"%repr(p.theChar))
		
	def testAttlistDecl(self):
		"""[52] AttlistDecl ::= '<!ATTLIST' S Name AttDef* S? '>' """
		s="""<!ATTLIST elem attr CDATA 'Steve' attr2 CDATA #IMPLIED>
		<!ATTLIST elem attr3 (1|2|3) '1'>
		 elem2 (1|2|3) >"""
		e=XMLEntity(s)
		p=XMLParser(e)
		p.dtd=XMLDTD()
		try:
			while True:
				p.ParseAttlistDecl(p.theChar!='<')
				p.ParseS()
		except XMLWellFormedError:
			pass
		aList=p.dtd.GetAttributeList('elem')
		self.assertTrue(aList['attr'].defaultValue=='Steve',"First attribute")
		self.assertTrue(aList['attr2'].presence==XMLAttributeDefinition.Implied,"Second attribute")
		self.assertTrue(aList['attr3'].type==XMLAttributeDefinition.Enumeration,"Third attribute")
		aList=p.dtd.GetAttributeList('elem2')
		self.assertTrue(aList is None,"Bad attribute")
		
	def testAttDef(self):
		"""[53] AttDef ::= S Name S AttType S DefaultDecl"""
		s=" attr CDATA 'Steve' attr2 CDATA #IMPLIED attr3 (1|2|3) '1' attr4 #REQUIRED"
		e=XMLEntity(s)
		m=[	('attr',XMLAttributeDefinition.CData,None,XMLAttributeDefinition.Default,'Steve'),
			('attr2',XMLAttributeDefinition.CData,None,XMLAttributeDefinition.Implied,None),
			('attr3',XMLAttributeDefinition.Enumeration,{'1':True,'2':True,'3':True},XMLAttributeDefinition.Default,'1')]
		p=XMLParser(e)
		for match in m:
			a=p.ParseAttDef()
			self.assertTrue(a.name==match[0],"AttDef match failed: %s (expected %s)"%(a.name,match[0]))
			self.assertTrue(a.type==match[1],"AttDef match failed: %i (expected %i)"%(a.type,match[1]))
			self.assertTrue(a.values==match[2],"AttDef match failed: %s (expected %s)"%(a.values,match[2]))
			self.assertTrue(a.presence==match[3],"AttDef match failed: %i (expected %i)"%(a.presence,match[3]))
			self.assertTrue(a.defaultValue==match[4],"AttDef match failed: %s (expected %s)"%(a.defaultValue,match[4]))
		try:
			a=p.ParseAttDef()
			self.fail("Parsed bad AttDef: %s"%a.name)
		except XMLFatalError:
			pass
		
	def testAttType(self):
		"""[54] AttType ::= StringType | TokenizedType | EnumeratedType"""
		s="CDATA ENTITIES NOTATION (Steve) (1 | 2 | 3) NAMES)"
		e=XMLEntity(s)
		m=[(XMLAttributeDefinition.CData,None),(XMLAttributeDefinition.Entities,None),(XMLAttributeDefinition.Notation,{'Steve':True}),
			(XMLAttributeDefinition.Enumeration,{'1':True,'2':True,'3':True})]
		p=XMLParser(e)
		for match in m:
			a=XMLAttributeDefinition()
			p.ParseAttType(a)
			self.assertTrue(a.type==match[0],"Attribute type match failed: %i (expected %i)"%(a.type,match[0]))
			self.assertTrue(a.values==match[1],"Attribute type match failed: %s (expected %s)"%(a.values,match[1]))
			p.ParseS()
		try:
			a=XMLAttributeDefinition()
			value=p.ParseAttType(a)
			self.fail("Parsed bad AttType: %i; %s"%(a.type,a.values))
		except XMLFatalError:
			pass
				
	def testStringType(self):
		"""[55] StringType ::= 'CDATA' """
		s="CDATA ID"
		e=XMLEntity(s)
		m=[(XMLAttributeDefinition.CData,None)]
		p=XMLParser(e)
		for match in m:
			a=XMLAttributeDefinition()
			p.ParseStringType(a)
			self.assertTrue(a.type==match[0],"String type match failed: %i (expected %i)"%(a.type,match[0]))
			self.assertTrue(a.values==match[1],"String type match failed: %s (expected %s)"%(a.values,match[1]))
			p.ParseS()
		try:
			a=XMLAttributeDefinition()
			value=p.ParseStringType(a)
			self.fail("Parsed bad StringType: %i; %s"%(a.type,a.values))
		except XMLFatalError:
			pass
		
	def testTokenizedType(self):
		"""[56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES'	 | 'NMTOKEN' | 'NMTOKENS' """
		s="ID IDREF IDREFS ENTITY ENTITIES NMTOKEN NMTOKENS NAME"
		e=XMLEntity(s)
		m=[(XMLAttributeDefinition.ID,None),(XMLAttributeDefinition.IDRef,None),(XMLAttributeDefinition.IDRefs,None),
			(XMLAttributeDefinition.Entity,None),(XMLAttributeDefinition.Entities,None),(XMLAttributeDefinition.NmToken,None),
			(XMLAttributeDefinition.NmTokens,None)]
		p=XMLParser(e)
		for match in m:
			a=XMLAttributeDefinition()
			p.ParseTokenizedType(a)
			self.assertTrue(a.type==match[0],"Tokenized type match failed: %i (expected %i)"%(a.type,match[0]))
			self.assertTrue(a.values==match[1],"Tokenized type match failed: %s (expected %s)"%(a.values,match[1]))
			p.ParseS()
		try:
			a=XMLAttributeDefinition()
			value=p.ParseTokenizedType(a)
			self.fail("Parsed bad Tokenized: %i; %s"%(a.type,a.values))
		except XMLFatalError:
			pass
		
	def testEnumeratedType(self):
		"""[57] EnumeratedType ::= NotationType | Enumeration """
		s="NOTATION (Steve1)NOTATION (Steve1|Steve2)(1|2|3)NOTATION (1|2|3)"
		e=XMLEntity(s)
		m=[(XMLAttributeDefinition.Notation,{'Steve1':True}),(XMLAttributeDefinition.Notation,{'Steve1':True,'Steve2':True}),(XMLAttributeDefinition.Enumeration,{'1':True,'2':True,'3':True})]
		p=XMLParser(e)
		for match in m:
			a=XMLAttributeDefinition()
			p.ParseEnumeratedType(a)
			self.assertTrue(a.type==match[0],"Enumerated type match failed: %i (expected %i)"%(a.type,match[0]))
			self.assertTrue(a.values==match[1],"Enumerated type match failed: %s (expected %s)"%(a.values,match[1]))
		try:
			a=XMLAttributeDefinition()
			value=p.ParseEnumeratedType(a)
			self.fail("Parsed bad EnumeratedType: %i; %s"%(a.type,a.values))
		except XMLFatalError:
			pass
		
	def testNotationType(self):
		"""[58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'"""
		s="NOTATION (Steve1)NOTATION (Steve1|Steve2|Steve3)NOTATION ( Steve1 ) ( Steve1 | Steve2 | Steve3 )NOTATION(Steve1|Steve2)"
		e=XMLEntity(s)
		m=[{'Steve1':True},{'Steve1':True,'Steve2':True,'Steve3':True},{'Steve1':True},{'Steve1':True,'Steve2':True,'Steve3':True}]
		p=XMLParser(e)
		for match in m:
			value=p.ParseNotationType(p.theChar!='N')
			self.assertTrue(value==match,"NotationType match failed: %s (expected %s)"%(value,match))
		try:
			value=p.ParseNotationType()
			self.fail("Parsed bad NotationType: %s"%value)
		except XMLFatalError:
			pass

	def testEnumeration(self):
		"""[59] Enumeration	   ::=   	'(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')' """
		s="(Steve1)(Steve1|Steve2|3Steve)( Steve1 )( Steve1 | Steve2 | 3Steve )(Steve1|Steve 2)"
		e=XMLEntity(s)
		m=[{'Steve1':True},{'Steve1':True,'Steve2':True,'3Steve':True},{'Steve1':True},{'Steve1':True,'Steve2':True,'3Steve':True}]
		p=XMLParser(e)
		for match in m:
			value=p.ParseEnumeration()
			self.assertTrue(value==match,"Enumeration match failed: %s (expected %s)"%(value,match))
		try:
			value=p.ParseEnumeration()
			self.fail("Parsed bad Enumeration: %s"%value)
		except XMLFatalError:
			pass

	def testDefaultDecl(self):
		"""[60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) """
		s="#REQUIRED #IMPLIED #FIXED 'Steve' 'Steve'Steve"
		e=XMLEntity(s)
		m=[(XMLAttributeDefinition.Required,None),(XMLAttributeDefinition.Implied,None),(XMLAttributeDefinition.Fixed,'Steve'),
			(XMLAttributeDefinition.Default,'Steve')]
		p=XMLParser(e)
		for match in m:
			a=XMLAttributeDefinition()
			p.ParseDefaultDecl(a)
			self.assertTrue(a.presence==match[0],"DefaultDecl declaration match failed: %i (expected %i)"%(a.presence,match[0]))
			self.assertTrue(a.defaultValue==match[1],"DefaultDecl value match failed: %s (expected %s)"%(a.defaultValue,match[1]))
			p.ParseS()
		try:
			a=XMLAttributeDefinition()
			p.ParseDefaultDecl(a)
			self.fail("Parsed bad DefaultDecl: (%i,%s)"%(a.presence,a.defaultValue))
		except XMLFatalError:
			pass
		
	def testConditionalSect(self):
		"""[61] conditionalSect ::= includeSect | ignoreSect"""
		s="<![%include;[ <!ENTITY included 'yes'> <![ IGNORE [ <!ENTITY ignored 'no'> ]]> ]]>"
		e=XMLEntity(s)
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('include','INCLUDE'))
		p.checkValidity=True
		p.refMode=XMLParser.RefModeInDTD
		try:
			p.ParseConditionalSect()
			self.assertTrue(p.theChar is None,"Short parse on ConditionalSect: %s"%p.theChar)
			self.assertTrue(p.dtd.GetEntity('included').definition=='yes',"included entity declaration")
			self.assertTrue(p.dtd.GetEntity('ignored')==None,"ignored entity declaration")			
		except XMLWellFormedError,e:
			self.fail("ParseConditionalSect positive test: %s\n%s"%(s,str(e)))
		
	def testIncludeSect(self):
		"""[62] includeSect ::= '<![' S? 'INCLUDE' S? '[' extSubsetDecl ']]>' """
		for s in ["<![INCLUDE[]]>","<![ INCLUDE [ <?stuff?> ]]>","<![ INCLUDE [<![IGNORE[ included ]]> ]]>",
			"<![%include;[]]>"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.dtd=XMLDTD()
			p.dtd.DeclareEntity(XMLParameterEntity('include','INCLUDE'))
			p.checkValidity=True
			p.refMode=XMLParser.RefModeInDTD
			try:
				p.ParseIncludeSect()
				self.assertTrue(p.theChar is None,"Short parse on IncludeSect: %s"%p.theChar)
			except XMLWellFormedError,e:
				self.fail("ParseIncludeSect positive test: %s\n%s"%(s,str(e)))
		for s in [" <![INCLUDE[]>","<! [INCLUDE[]]>","<![INCLUDE[] ]>"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.refMode=XMLParser.RefModeInDTD
			try:
				p.ParseIncludeSect()
				self.fail("ParseIncludeSect negative well-formedness test: %s"%s)
			except XMLWellFormedError:
				pass
		for s in ["<![ %include1; <?stuff?> ]]>","%include2; [ <!--stuff--> ]]>",
			"<![ INCLUDE [ <?stuff?> %include3;"
			]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.refMode=XMLParser.RefModeInContent
			p.dtd=XMLDTD()
			p.dtd.DeclareEntity(XMLParameterEntity('include1','INCLUDE ['))			
			p.dtd.DeclareEntity(XMLParameterEntity('include2','<![INCLUDE '))			
			p.dtd.DeclareEntity(XMLParameterEntity('include3','<?included?> ]]>'))		
			p.checkValidity=True
			p.raiseValidityErrors=True
			p.refMode=XMLParser.RefModeInDTD
			try:
				p.ParseS()
				p.ParseIncludeSect()
				self.fail("ParseIncludeSect negative validity test: %s"%s)
			except XMLWellFormedError,e:
				logging.info(str(e))
				self.fail("ParseIncludeSect spurious well-formed error: %s"%s)
			except XMLValidityError:
				pass

	def testIgnoreSect(self):
		"""[63] ignoreSect ::= '<![' S? 'IGNORE' S? '[' ignoreSectContents* ']]>' """
		for s in ["<![IGNORE[]]>","<![ IGNORE [ stuff ]]>","<![ IGNORE [<![INCLUDE[ ignored ]]> ]]>",
			"<![%ignore;[]]>"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.dtd=XMLDTD()
			p.dtd.DeclareEntity(XMLParameterEntity('ignore','IGNORE'))
			p.checkValidity=True
			p.refMode=XMLParser.RefModeInDTD
			try:
				p.ParseIgnoreSect()
				self.assertTrue(p.theChar is None,"Short parse on IgnoreSect: %s"%p.theChar)
			except XMLWellFormedError:
				self.fail("ParseIgnoreSect positive test: %s"%s)
		for s in [" <![IGNORE[]>","<! [IGNORE[]]>","<![IGNORE[] ]>"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.refMode=XMLParser.RefModeInDTD
			try:
				p.ParseIgnoreSect()
				self.fail("ParseIgnoreSect negative well-formedness test: %s"%s)
			except XMLWellFormedError:
				pass
		for s in ["<![ %ignore1; stuff ]]>","%ignore2; [ stuff ]]>",
			# "<![ IGNORE [ stuff %ignore3;" - this PE is ignored so we can't test this
			]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.refMode=XMLParser.RefModeInContent
			p.dtd=XMLDTD()
			p.dtd.DeclareEntity(XMLParameterEntity('ignore1','IGNORE ['))			
			p.dtd.DeclareEntity(XMLParameterEntity('ignore2','<![IGNORE '))			
			p.dtd.DeclareEntity(XMLParameterEntity('ignore3','ignored ]]>'))		
			p.checkValidity=True
			p.raiseValidityErrors=True
			p.refMode=XMLParser.RefModeInDTD
			try:
				p.ParseS()
				p.ParseIgnoreSect()
				self.fail("ParseIgnoreSect negative validity test: %s"%s)
			except XMLWellFormedError,e:
				logging.info(str(e))
				self.fail("ParseIgnoreSect spurious well-formed error: %s"%s)
			except XMLValidityError:
				pass
		
	def testIgnoreSectContents(self):
		"""[64] ignoreSectContents ::= Ignore ('<![' ignoreSectContents ']]>' Ignore)* """
		s="preamble<![ INCLUDE [ %x; <![IGNORE[ also ignored ]]>]]> also ignored]]>end"
		e=XMLEntity(s)
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('x','bad'))
		p.ParseIgnoreSectContents()
		p.ParseLiteral(']]>')
		self.assertTrue(p.ParseName()=='end',"Failed to parse ignore section contents")
		
	def testIgnore(self):
		"""[65] Ignore ::= Char* - (Char* ('<![' | ']]>') Char*) """
		s="<!FIRST%x;1st]]>second<![third]]3rd<!3rd<3rd]3rd"
		e=XMLEntity(s)
		# These tests are a bit odd because we follow the entity and not the parser
		# so we expect the trailing markup to be consumed; we check theChar too to be sure.
		m=[('<!FIRST%x;1st]]>',']'),('second<![','<'),('third]]3rd<!3rd<3rd]3rd',None)]
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('x','bad'))
		pos=0
		for match,c in m:
			p.ParseIgnore()
			self.assertTrue(p.theChar==c,"Parser position: %s (expected %s)"%(p.theChar,c))
			self.assertTrue(s[pos:e.linePos-1]==match,"Match failed: %s (expected %s)"%(s[pos:e.linePos-1],match))
			p.NextChar()
			pos=pos+len(match)
		
	def testCharRef(self):
		"""[66] CharRef ::= '&#' [0-9]+ ';'  |  '&#x' [0-9a-fA-F]+ ';' """
		for m in (XMLParser.RefModeInContent,XMLParser.RefModeInAttributeValue,XMLParser.RefModeInEntityValue):
			e=XMLEntity("&#xe9;")
			p=XMLParser(e)
			p.refMode=m
			data=p.ParseCharRef()
			self.assertTrue(data==u'\xe9',"ParseCharRef failed to interpret hex character reference: %s"%data)
			self.assertTrue(p.theChar is None,"Short parse on CharRef: %s"%p.theChar)
		e=XMLEntity("&#xe9;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeAsAttributeValue
		data=p.ParseCharRef()
		self.assertTrue(data=="&#xe9;","ParseCharRef AsAttribute: %s"%data)
		self.assertTrue(p.theChar is None,"Short parse on CharRef: %s"%p.theChar)
		e=XMLEntity("&#xe9;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInDTD
		try:
			data=p.ParseCharRef()
			self.fail("ParseCharRef InDTD")
		except XMLForbiddenEntityReference:
			pass
		e=XMLEntity("#233;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInContent
		data=p.ParseCharRef(True)
		self.assertTrue(data==u'\xe9',"ParseCharRef failed to interpret decimal character reference: %s"%data)
		self.assertTrue(p.theChar is None,"Short parse on CharRef: %s"%p.theChar)
		for s in [" &#xe9;","& #xe9;","&# xe9;","&#xe 9;","&#xe9 ;","&#e9;","&#xg9;","&#1;","&#;","&#x;"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.refMode=XMLParser.RefModeInContent
			try:
				p.ParseCharRef()
				self.fail("ParseCharRef negative test: %s"%s)
			except XMLWellFormedError:
				pass					
		
	def testReference(self):
		"""[67] Reference ::= EntityRef | CharRef """
		e=XMLEntity("&animal;")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLGeneralEntity('animal','dog'))
		p.refMode=XMLParser.RefModeInContent
		data=p.ParseReference()
		self.assertTrue(data=='',"ParseReference failed to interpret entity reference")
		self.assertTrue(p.ParseName()=='dog',"Failed to replace Entity in Content")
		self.assertTrue(p.theChar is None,"Short parse on EntityRef")
		e=XMLEntity("&#xe9;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInContent
		data=p.ParseReference()
		self.assertTrue(data==u'\xe9',"ParseReference failed to interpret character reference: %s"%data)
		self.assertTrue(p.theChar is None,"Short parse on EntityRef: %s"%p.theChar)
		for s in [" &animal;","& animal;","&animal ;","animal","#xE9"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseReference()
				self.fail("ParseReference negative test: %s"%s)
			except XMLWellFormedError:
				pass	
		
	def testEntityRef(self):
		"""[68] EntityRef ::= '&' Name ';'	"""
		e=XMLEntity("&amp;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInContent
		self.assertTrue(p.ParseEntityRef()=='&',"Predefined entity not recognized in Content")
		self.assertTrue(p.theChar is None,"Short parse on Entity replacement text")
		e=XMLEntity("&animal;")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLGeneralEntity('animal','dog'))
		p.refMode=XMLParser.RefModeInContent
		self.assertTrue(p.ParseEntityRef()=='',"EntityRef not recognized in Content")
		# This should result in the entity value being expanded into the stream
		self.assertTrue(p.ParseName()=='dog',"Failed to replace Entity in Content")
		self.assertTrue(p.theChar is None,"Short parse on Entity replacement text")
		e=XMLEntity("animal;")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLGeneralEntity('animal','dog'))
		p.refMode=XMLParser.RefModeInAttributeValue
		self.assertTrue(p.ParseEntityRef(True)=='',"EntityRef not recognized in Attribute Value")
		# This should result in the entity value being expanded into the stream
		self.assertTrue(p.ParseName()=='dog',"Failed to replace Entity in Attribute Vaue")
		self.assertTrue(p.theChar is None,"Short parse on Entity replacement text")
		e=XMLEntity("&animal;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeAsAttributeValue
		try:
			p.ParseEntityRef()
			self.fail("EntityRef recognized as Attribute Value")
		except XMLForbiddenEntityReference:
			pass
		e=XMLEntity("&animal;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInEntityValue
		data=p.ParseEntityRef()
		self.assertTrue(data=='&animal;',"EntityRef recognized in EntityValue: %s"%data)
		self.assertTrue(p.theChar is None,"Short parse on EntityRef")
		e=XMLEntity("&animal;")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInDTD
		try:
			p.ParseEntityRef()
			self.fail("EntityRef recognized in DTD")
		except XMLForbiddenEntityReference:
			pass
		e=XMLEntity("<element attribute='a-&EndAttr;>")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('EndAttr',"27'"))
		try:
			p.ParseSTag()
			self.fail("EntityRef quote test failed in attribute value")
		except XMLWellFormedError:
			pass
		
	def testPEReference(self):
		"""[69] PEReference ::= '%' Name ';' """
		e=XMLEntity("%animal;")
		p=XMLParser(e)
		p.checkValidity=True
		p.refMode=XMLParser.RefModeInContent
		data=p.ParsePEReference()
		self.assertTrue(data=='%animal;',"PEReference recognized in content: %s"%data)
		self.assertTrue(p.theChar is None,"Short parse on PEReference")
		e=XMLEntity("%animal;")
		p=XMLParser(e)
		p.checkValidity=True
		p.refMode=XMLParser.RefModeInAttributeValue
		self.assertTrue(p.ParsePEReference()=='%animal;',"PEReference recognized in attribute value")
		self.assertTrue(p.theChar is None,"Short parse on PEReference")
		e=XMLEntity("%animal;")
		p=XMLParser(e)
		p.checkValidity=True
		p.refMode=XMLParser.RefModeAsAttributeValue
		self.assertTrue(p.ParsePEReference()=="%animal;","PEReference recognized as attribute value")
		self.assertTrue(p.theChar is None,"Short parse on PEReference")
		e=XMLEntity("%animal;")
		p=XMLParser(e)
		p.checkValidity=True
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('animal','dog'))
		p.refMode=XMLParser.RefModeInEntityValue
		self.assertTrue(p.ParsePEReference()=='',"PEReference not recognized in entity value")
		# This should result in the entity value being expanded into the stream
		self.assertTrue(p.ParseName()=='dog',"Failed to replace PE in entity value")
		self.assertTrue(p.theChar is None,"Short parse on PEReference replacement text")
		e=XMLEntity("%animal;")
		p=XMLParser(e)
		p.checkValidity=True
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('animal','dog'))
		p.refMode=XMLParser.RefModeInDTD
		self.assertTrue(p.ParsePEReference()=='',"PEReference not recognized in DTD")
		# This should result in the entity value being expanded into the stream with surrounding spaces
		self.assertTrue(p.ParseS()==' ',"Missing leading space on PE replacement text")
		self.assertTrue(p.ParseName()=='dog',"Failed to replace PE in DTD")
		self.assertTrue(p.ParseS()==' ',"Missing trailing space on PE replacement text")
		self.assertTrue(p.theChar is None,"Short parse on PEReference")
		e=XMLEntity('<!ENTITY WhatHeSaid "He said %YN;" >')
		p=XMLParser(e)
		p.checkValidity=True
		p.dtd=XMLDTD()
		p.dtd.DeclareEntity(XMLParameterEntity('YN','"Yes"'))
		try:
			ge=p.ParseEntityDecl()
			# This should result in the entity value being expanded into the stream with surrounding spaces
			self.assertTrue(ge.definition=='He said "Yes"',"PEReference quote test failed in entity value: %s"%ge.definition)
			self.assertTrue(p.theChar is None,"Short parse on PEReference in entity declaration")
		except XMLWellFormedError:
			self.fail("PEReference quote test failed in entity value")
	
		
	def testEntityDecl(self):
		"""[70] EntityDecl ::= GEDecl | PEDecl """
		e=XMLEntity("<!ENTITY Steve 'SteveValue'>")
		p=XMLParser(e)
		ed=p.ParseEntityDecl()
		self.assertTrue(isinstance(ed,XMLGeneralEntity),"ParseEntityDecl failed to return GeneralEntity")		
		self.assertTrue(ed.name=='Steve',"Failed to parse general entity name")
		self.assertTrue(p.theChar is None,"Short parse on EntityDecl")
		e=XMLEntity(" % Steve 'SteveValue'>")
		p=XMLParser(e)
		ed=p.ParseEntityDecl(True)
		self.assertTrue(isinstance(ed,XMLParameterEntity),"ParseEntityDecl failed to return ParameterEntity")		
		self.assertTrue(ed.name=='Steve',"Failed to parse parameter entity name")
		self.assertTrue(p.theChar is None,"Short parse on EntityDecl")
		
	def testGEDecl(self):
		"""[71] GEDecl ::= '<!ENTITY' S Name S EntityDef S? '>' """
		e=XMLEntity("<!ENTITY Steve 'SteveValue'>")
		p=XMLParser(e)
		ge=p.ParseGEDecl()
		self.assertTrue(isinstance(ge,XMLGeneralEntity),"ParseGEDecl failed to return GeneralEntity")		
		self.assertTrue(ge.name=='Steve',"Failed to parse general entity name")
		self.assertTrue(ge.definition=='SteveValue',"Failed to parse general entity value")
		self.assertTrue(p.theChar is None,"Short parse on GEDecl")
		e=XMLEntity("Steve PUBLIC 'Steve' '/home/steve.txt' NDATA SteveN  >")
		p=XMLParser(e)
		ge=p.ParseGEDecl(True)
		self.assertTrue(ge.definition.public=='Steve',"ParseGEDecl failed to parse external public ID")
		self.assertTrue(ge.definition.system=='/home/steve.txt',"ParseGEDecl failed to parse external system ID")
		self.assertTrue(ge.notation=='SteveN',"ParseGEDecl failed to parse unparsed entity notation")
		self.assertTrue(p.theChar is None,"Short parse on GEDecl")
		for s in ["<!entity Steve 'v'>","<!ENTITYSteve 'v'>",
			"<!ENTITY Steve PUBLIC 'Steve' '/home/steve.txt'NDATA SteveN >",
			"  Steve PUBLIC 'Steve' '/home/steve.txt' NDATA SteveN  >"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				ge=p.ParseGEDecl(s[0]!='<')
				self.fail("GEDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass	
	
	def testPEDecl(self):
		"""[72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>' """
		e=XMLEntity("<!ENTITY % Steve 'SteveValue'>")
		p=XMLParser(e)
		p.refMode=XMLParser.RefModeInDTD
		pe=p.ParsePEDecl()
		self.assertTrue(isinstance(pe,XMLParameterEntity),"ParsePEDecl failed to return ParameterEntity")		
		self.assertTrue(pe.name=='Steve',"Failed to parse parameter entity name")
		self.assertTrue(pe.definition=='SteveValue',"Failed to parse parameter entity value")
		self.assertTrue(p.theChar is None,"Short parse on PEDecl")
		e=XMLEntity("% Steve PUBLIC 'Steve' '/home/steve.txt'   >")
		p=XMLParser(e)
		pe=p.ParsePEDecl(True)
		self.assertTrue(pe.definition.public=='Steve',"ParsePEDecl failed to parse external public ID")
		self.assertTrue(pe.definition.system=='/home/steve.txt',"ParsePEDecl failed to parse external system ID")
		self.assertTrue(p.theChar is None,"Short parse on PEDecl")
		for s in ["<!entity % Steve 'v'>","<!ENTITY% Steve 'v'>","<!ENTITY %Steve 'v'>","<!ENTITY % Steve'v'>",
			"  % Steve PUBLIC 'Steve' '/home/steve.txt'   >"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				pe=p.ParsePEDecl(s[0]!='<')
				self.fail("PEDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass	
		
	def testEntityDef(self):
		"""[73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) """
		e=XMLEntity("'Steve'")
		p=XMLParser(e)
		ge=XMLGeneralEntity()
		p.ParseEntityDef(ge)
		self.assertTrue(type(ge.definition) in StringTypes,"ParseEntityDef failed to for internal entity")		
		self.assertTrue(ge.definition=='Steve',"Failed to parse internal entity value")
		self.assertTrue(ge.notation is None,"Found notation for internal entity")
		self.assertTrue(p.theChar is None,"Short parse on EntityDef")
		e=XMLEntity("PUBLIC 'Steve' '/home/steve.txt'")
		p=XMLParser(e)
		ge=XMLGeneralEntity()
		p.ParseEntityDef(ge)
		self.assertTrue(isinstance(ge.definition,XMLExternalID),"ParseEntityDef failed for external entity")
		self.assertTrue(ge.definition.public=='Steve',"ParseEntityDef failed to parse external public ID")
		self.assertTrue(ge.definition.system=='/home/steve.txt',"ParseEntityDef failed to parse external system ID")
		self.assertTrue(ge.notation is None,"Found notation for internal entity")
		self.assertTrue(p.theChar is None,"Short parse on EntityDef")
		e=XMLEntity("SYSTEM '/path' NDATA SteveN")
		p=XMLParser(e)
		ge=XMLGeneralEntity()
		p.ParseEntityDef(ge)
		self.assertTrue(ge.definition.public is None,"ParseEntityDef found spurious public ID")
		self.assertTrue(ge.definition.system=='/path',"ParseEntityDef failed to parse external system ID")
		self.assertTrue(ge.notation=='SteveN',"Failed to find notation for unparsed external entity")
		self.assertTrue(p.theChar is None,"Short parse on EntityDef")
		for s in ["NDATA 'SteveN'"," 'Steve'"," SYSTEM '/path'"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			ge=XMLGeneralEntity()
			try:
				p.ParseEntityDef(ge)
				self.fail("EntityDef negative test: %s"%s)
			except XMLWellFormedError:
				pass	
		
	def testPEDef(self):
		"""[74] PEDef ::= EntityValue | ExternalID """
		e=XMLEntity("PUBLIC 'Steve' '/home/steve.txt'")
		p=XMLParser(e)
		pe=XMLParameterEntity()
		p.ParsePEDef(pe)
		self.assertTrue(isinstance(pe.definition,XMLExternalID),"ParsePEDef failed to return XMLExternalID instance")
		self.assertTrue(pe.definition.public=='Steve',"Failed to parse external public ID")
		self.assertTrue(pe.definition.system=='/home/steve.txt',"Failed to parse external system ID")
		self.assertTrue(p.theChar is None,"Short parse on PEDef")
		e=XMLEntity("'Steve'")
		p=XMLParser(e)
		pe=XMLParameterEntity()
		p.ParsePEDef(pe)
		self.assertTrue(type(pe.definition) in StringTypes,"ParsePEDef failed to return String value")
		self.assertTrue(pe.definition=='Steve',"Failed to parse simple entity value")
		self.assertTrue(p.theChar is None,"Short parse on PEDef")
		e=XMLEntity('"Caf&#xE9;s &amp; Bars"')
		p=XMLParser(e)
		pe=XMLParameterEntity()
		p.ParsePEDef(pe)
		self.assertTrue(type(pe.definition) in StringTypes,"ParsePEDef failed to return String value")
		self.assertTrue(pe.definition==u'Caf\xe9s &amp; Bars',"Failed to replace character entities: %s"%repr(pe.definition))
		self.assertTrue(p.theChar is None,"Short parse on PEDef")
		for s in ["Steve","Caf&#xE9;s &amp; Bars","PUBLIC 'Steve'"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			pe=XMLParameterEntity()
			try:
				p.ParsePEDef(pe)
				self.fail("PEDef negative test: %s"%s)
			except XMLWellFormedError:
				pass
		
	def testExternalID(self):
		"""[75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral """
		e=XMLEntity("PUBLIC 'Steve' '/home/steve.txt'")
		p=XMLParser(e)
		xID=p.ParseExternalID()
		self.assertTrue(xID.public=='Steve',"Failed to parse external public ID")
		self.assertTrue(xID.system=='/home/steve.txt',"Failed to parse external system ID")
		self.assertTrue(p.theChar is None,"Short parse on ExternalID")
		e=XMLEntity("SYSTEM  '/home/steve.txt'")
		p=XMLParser(e)
		xID=p.ParseExternalID()
		self.assertTrue(xID.public is None,"Failed to parse external empty public ID")
		self.assertTrue(xID.system=='/home/steve.txt',"Failed to parse external system ID")
		self.assertTrue(p.theChar is None,"Short parse on ExternalID")
		for s in ["PUBLIC 'Steve'","'Steve'"," SYSTEM '/path'","SYSTEM'/path'","PUBLIC'Steve' '/path'",
			"PUBLIC 'Steve''/path'"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				xID=p.ParseExternalID()
				self.fail("ExternalID negative test: %s"%s)
			except XMLWellFormedError:
				pass
		
	def testNDataDecl(self):
		"""[76] NDataDecl ::= S 'NDATA' S Name """
		e=XMLEntity("  NDATA Steve")
		p=XMLParser(e)
		self.assertTrue(p.ParseNDataDecl()=="Steve","Failed to parse NData declaration")
		self.assertTrue(p.theChar is None,"Short parse on NData declaration")
		e=XMLEntity(" Steve")
		p=XMLParser(e)
		self.assertTrue(p.ParseNDataDecl(True)=="Steve","Failed to parse NData declaration (no literal)")
		self.assertTrue(p.theChar is None,"Short parse on NData declaration")
		for s in ["NDATA Steve"," MDATA Steve","NDATASteve"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseNDataDecl()
				self.fail("NDataDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass
		
	def testTextDecl(self):
		"""[77] TextDecl ::= '<?xml' VersionInfo? EncodingDecl S? '?>' """
		e=XMLEntity("<?xml version='1.0' encoding='latin-1'  ?>")
		p=XMLParser(e)
		t=p.ParseTextDecl()
		self.assertTrue(t.version=="1.0")
		self.assertTrue(t.encoding=="latin-1","Failed to parse encoding in text declaration")
		self.assertTrue(p.theChar is None,"Short parse on TextDecl")
		e=XMLEntity('<?xml encoding = "latin-1"?>')
		p=XMLParser(e)
		t=p.ParseTextDecl()
		self.assertTrue(t.version is None)
		self.assertTrue(t.encoding=="latin-1","Failed to parse encoding in text declaration")
		self.assertTrue(p.theChar is None,"Short parse on TextDecl")
		for s in ["<?xml version='1.0' ?>"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseEncodingDecl()
				self.fail("TextDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass
		
	# There is no method for parsing production [78]
	
	# There is no production [79]
	
	def testEncodingDecl(self):
		"""[80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName "'" ) """
		e=XMLEntity("  encoding = 'latin-1'")
		p=XMLParser(e)
		self.assertTrue(p.ParseEncodingDecl()=="latin-1","Failed to parse encoding declaration")
		self.assertTrue(p.theChar is None,"Short parse on EncodingDecl")
		e=XMLEntity(" = 'latin-1'")
		p=XMLParser(e)
		self.assertTrue(p.ParseEncodingDecl(True)=="latin-1","Failed to parse encoding declaration (no literal)")
		self.assertTrue(p.theChar is None,"Short parse on EncodingDecl")
		e=XMLEntity(' encoding="latin-1"')
		p=XMLParser(e)
		self.assertTrue(p.ParseEncodingDecl()=="latin-1","Failed to parse encoding declaration")
		self.assertTrue(p.theChar is None,"Short parse on EncodingDecl")
		for s in ["encoding = 'latin-1'"," decoding='latin-1'"," encoding=latin-1"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParseEncodingDecl()
				self.fail("EncodingDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass

	def testEncName(self):
		"""[81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')* """
		e=XMLEntity("UTF-8 UTF-16 ISO-10646-UCS-2 ISO-10646-UCS-4 Shift_JIS -8 _JIS .Private x.Private")
		result=["UTF-8","UTF-16","ISO-10646-UCS-2","ISO-10646-UCS-4","Shift_JIS","JIS","Private","x.Private"]
		p=XMLParser(e)
		i=0
		while p.theChar!=None:
			eName=p.ParseEncName()
			if eName:
				self.assertTrue(eName==result[i],"%s parsed, expected %s"%(eName,result[i]))
				p.ParseS()
				i=i+1
			else:
				p.NextChar()		
		
	def testCaseNotationDecl(self):
		"""[82] NotationDecl ::= '<!NOTATION' S Name S (ExternalID | PublicID) S? '>'"""
		e=XMLEntity("<!NOTATION SteveN PUBLIC 'Steve' '/home/steve.txt'>")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.ParseNotationDecl()
		n=p.dtd.GetNotation('SteveN')
		self.assertTrue(n.name=='SteveN',"Failed to parse notation name")
		self.assertTrue(n.externalID.public=='Steve',"Failed to parse notation public ID")
		self.assertTrue(n.externalID.system=='/home/steve.txt',"Failed to parse notation system ID")
		self.assertTrue(p.theChar is None,"Short parse on NotationDecl")
		e=XMLEntity(" SteveN PUBLIC 'Steve' >")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.ParseNotationDecl(True)
		n=p.dtd.GetNotation('SteveN')
		self.assertTrue(n.name=='SteveN',"Failed to parse notation name")
		self.assertTrue(n.externalID.public=='Steve',"Failed to parse notation public ID")
		self.assertTrue(n.externalID.system is None,"Failed to parse empty notation system ID")
		self.assertTrue(p.theChar is None,"Short parse on NotationDecl")
		e=XMLEntity("<!NOTATION SteveN SYSTEM  '/home/steve.txt' >")
		p=XMLParser(e)
		p.dtd=XMLDTD()
		p.ParseNotationDecl()
		n=p.dtd.GetNotation('SteveN')
		self.assertTrue(n.name=='SteveN',"Failed to parse notation name")
		self.assertTrue(n.externalID.public is None,"Failed to parse empty notation public ID")
		self.assertTrue(n.externalID.system=='/home/steve.txt',"Failed to parse notation system ID")
		self.assertTrue(p.theChar is None,"Short parse on NotationDecl")
		for s in ["SteveN PUBLIC 'Steve' >"," 'SteveN' PUBLIC 'Steve' >","SteveN 'Steve' >",
			"SteveN PUBLIC >","SteveN SYSTEM>","SteveN SYSTEM 'Steve' '/path'>","SteveN PUBLIC 'Steve' "]:
			e=XMLEntity(s)
			p=XMLParser(e)
			p.dtd=XMLDTD()
			try:
				p.ParseNotationDecl(True)
				self.fail("NotationDecl negative test: %s"%s)
			except XMLWellFormedError:
				pass

	def testCasePublicID(self):
		"""[83] PublicID ::= 'PUBLIC' S PubidLiteral"""
		e=XMLEntity("PUBLIC 'Steve'")
		p=XMLParser(e)
		self.assertTrue(p.ParsePublicID()=='Steve',"Failed to parse Public ID")
		self.assertTrue(p.theChar is None,"Short parse on Public ID")
		for s in [" PUBLIC 'Steve'","'Steve'","PUBLIC'Steve'","Public 'Steve'"]:
			e=XMLEntity(s)
			p=XMLParser(e)
			try:
				p.ParsePublicID()
				self.fail("PublicID negative test: %s"%s)
			except XMLWellFormedError:
				pass
		

class ElementTests(unittest.TestCase):
	def testCaseConstructor(self):
		e=Element(None)
		self.assertTrue(e.xmlname==None,'element name on construction')
		self.assertTrue(e.GetDocument() is None,'document set on construction')
		attrs=e.GetAttributes()
		self.assertTrue(len(attrs.keys())==0,"Attributes present on construction")
		children=e.GetChildren()
		try:
			children.next()
			self.fail("Children present on construction")
		except StopIteration:
			pass
		e=Element(None,'test')
		self.assertTrue(e.xmlname=='test','element named on construction')
		
	def testCaseDefaultName(self):
		e=NamedElement(None)
		self.assertTrue(e.xmlname=='test','element default name on construction')
	
	def testSetXMLName(self):
		e=NamedElement(None,'test2')
		self.assertTrue(e.xmlname=='test2','element named explicitly in construction')

	def testAttributes(self):
		e=Element(None,'test')
		e.SetAttribute('atest','value')
		attrs=e.GetAttributes()
		self.assertTrue(len(attrs.keys())==1,"Attribute not set")
		self.assertTrue(attrs['atest']=='value',"Attribute not set correctly")
		e=ReflectiveElement(None)
		e.SetAttribute('atest','value')
		# Deprecated: self.assertTrue(e.atest=='value',"Attribute relfection")
		attrs=e.GetAttributes()
		self.assertTrue(attrs['atest']=='value',"Attribute not set correctly")
		e.SetAttribute('btest','Yes')
		self.assertTrue(e.bTest=='Yes',"Attribute relfection with simple assignment")
		attrs=e.GetAttributes()
		self.assertTrue(attrs['btest']=='Yes',"Attribute not set correctly")
		e.SetAttribute('ctest','Yes')
		self.assertTrue(e.cTest==True,"Attribute relfection with decode/encode")
		attrs=e.GetAttributes()
		self.assertTrue(attrs['ctest']=='Yes',"Attribute not set correctly")
		self.assertFalse('dtest' in attrs,"Optional ordered list attribute")
		self.assertTrue(attrs['dtestR']=='',"Required ordered list attribute") 
		e.SetAttribute('dtest','Yes No')
		self.assertTrue(e.dTest==[True,False],"Attribute relfection with list; %s"%repr(e.dTest))
		attrs=e.GetAttributes()
		self.assertTrue(attrs['dtest']=='Yes No',"Attribute not set correctly")
		self.assertFalse('etest' in attrs,"Optional unordered list attribute") 
		self.assertTrue(attrs['etestR']=='',"Required unordered list attribute") 
		e.SetAttribute('etest','Yes No Yes')
		self.assertTrue(e.eTest=={True:2,False:1},"Attribute relfection with list: %s"%repr(e.eTest))
		attrs=e.GetAttributes()
		self.assertTrue(attrs['etest']=='No Yes Yes',"Attribute not set correctly: %s"%repr(attrs['etest']))
		try:
			if e.ztest:
				pass
			self.fail("AttributeError required for undefined names")
		except AttributeError:
			pass
		e.ztest=1
		if e.ztest:
			pass
		del e.ztest
		try:
			if e.ztest:
				pass
			self.fail("AttributeError required for undefined names after del")
		except AttributeError:
			pass
		try:
			self.assertTrue(e.fTest is None,"Missing attribute auto value not None")
		except AttributeError:
			self.fail("Missing attribute auto value: AttributeError")
		e.fTest=1
		del e.fTest
		try:
			self.assertTrue(e.fTest is None,"Missing attribute auto value not None (after del)")
		except AttributeError:
			self.fail("Missing attribute auto value: AttributeError (after del)")
	
	def testChildElements(self):
		"""Test child element behaviour"""
		e=Element(None,'test')
		child1=e.ChildElement(Element,'test1')
		children=list(e.GetChildren())
		self.assertTrue(len(children)==1,"ChildElement failed to add child element")
	
	def testChildElementReflection(self):
		"""Test child element cases using reflection"""
		e=ReflectiveElement(None)
		child1=e.ChildElement(ReflectiveElement,'test1')
		self.assertTrue(e.child is child1,"Element not set by reflection")
		children=list(e.GetChildren())
		self.assertTrue(len(children)==1 and children[0] is child1,"ChildElement failed to add child element")
		# Now create a second child, should return the same one due to model restriction
		child2=e.ChildElement(ReflectiveElement,'test1')
		self.assertTrue(e.child is child1 and child2 is child1,"Element model violated")
		child3=e.ChildElement(GenericElementA,'test3')
		self.assertTrue(e.generics[0] is child3,"Generic element")
		child4=e.ChildElement(GenericSubclassA,'test4')
		self.assertTrue(e.generics[1] is child4,"Generic sub-class element via method")
		child5=e.ChildElement(GenericSubclassB,'test5')
		self.assertTrue(e.GenericElementB is child5,"Generic sub-class element via member")
		
	def testData(self):
		e=Element(None)
		self.assertTrue(e.IsMixed(),"Mixed default")
		e.AddData('Hello')
		self.assertTrue(e.GetValue()=='Hello',"Data value")
		children=list(e.GetChildren())
		self.assertTrue(len(children)==1,"Data child not set")
		self.assertTrue(children[0]=="Hello","Data child not set correctly")
	
	def testEmpty(self):
		e=EmptyElement(None)
		self.assertFalse(e.IsMixed(),"EmptyElement is mixed")
		self.assertTrue(e.IsEmpty(),"EmptyElement not empty")
		try:
			e.AddData('Hello')
			self.fail("Data in EmptyElement")
		except XMLValidityError:
			pass
		try:
			child=e.ChildElement(Element)
			self.fail("Elements allowed in EmptyElement")
		except XMLValidityError:
			pass		

	def testElementContent(self):	
		e=ElementContent(None)
		self.assertFalse(e.IsMixed(),"ElementContent appears mixed")
		self.assertFalse(e.IsEmpty(),"ElementContent appears empty")
		try:
			e.AddData('Hello')
			self.fail("Data in ElementContent")
		except XMLValidityError:
			pass
		# white space should silently be ignored.
		e.AddData('  \n\r  \t')
		children=list(e.GetChildren())
		self.assertTrue(len(children)==0,"Unexpected children")
		# elements can be added
		child=e.ChildElement(Element)
		children=list(e.GetChildren())
		self.assertTrue(len(children)==1,"Expected one child")
	
	def testMixedContent(self):
		e=MixedElement(None)
		self.assertTrue(e.IsMixed(),"MixedElement not mixed")
		self.assertFalse(e.IsEmpty(),"MixedElement appears empty")
		e.AddData('Hello')
		self.assertTrue(e.GetValue()=='Hello','Mixed content with a single value')
		child=e.ChildElement(Element)
		try:
			e.GetValue()
		except XMLMixedContentError:
			pass
		
	def testCopy(self):
		e1=Element(None)
		e2=e1.Copy()
		self.assertTrue(isinstance(e2,Element),"Copy didn't make Element")
		self.assertTrue(e1==e2 and e1 is not e2)
		

class DocumentTests(unittest.TestCase):
	def setUp(self):
		self.cwd=os.getcwd()
		self.d=mkdtemp('.d','pyslet-test_xml20081126-')
		os.chdir(self.d)
		
	def tearDown(self):
		os.chdir(self.cwd)
		shutil.rmtree(self.d,True)

	def testCaseConstructor(self):
		d=Document()
		self.assertTrue(d.root is None,'root on construction')
		self.assertTrue(d.GetBase() is None,'base set on construction')
		d=Document(root=Element)
		self.assertTrue(isinstance(d.root,Element),'root not created on construction')
		self.assertTrue(d.root.GetDocument() is d,'root not linked to document')
	
	def testCaseBase(self):
		"""Test the use of a file path on construction"""
		fpath=os.path.abspath('fpath.xml')
		furl=str(URIFactory.URLFromPathname(fpath))
		d=Document(baseURI=furl)
		self.assertTrue(d.GetBase()==furl,"Base not set in constructor")
		self.assertTrue(d.root is None,'root on construction')
		d=Document(baseURI='fpath.xml',root=Element)
		self.assertTrue(d.GetBase()==furl,"Base not made absolute from relative URL:\n\t%s\n\t%s"%(furl,d.GetBase()))
		self.assertTrue(isinstance(d.root,Element),'root not created on construction')
		d=Document()
		d.SetBase(furl)
		self.assertTrue(d.GetBase()==furl,"Base not set by SetBase")

	def testCaseReadFile(self):
		"""Test the reading of the Document from the file system"""
		os.chdir(TEST_DATA_DIR)
		d=Document(baseURI='readFile.xml')
		d.Read()
		root=d.root
		self.assertTrue(isinstance(root,Element))
		self.assertTrue(root.xmlname=='tag' and root.GetValue()=='Hello World')
		
	def testCaseReadString(self):
		"""Test the reading of the Document from a supplied stream"""
		os.chdir(TEST_DATA_DIR)
		d=Document(baseURI='readFile.xml')
		f=open('readFile.xml')
		d.Read(src=f)
		f.close()
		root=d.root
		self.assertTrue(isinstance(root,Element))
		self.assertTrue(root.xmlname=='tag' and root.GetValue()=='Hello World')
	
	def testCaseString(self):
		os.chdir(TEST_DATA_DIR)
		d=Document(baseURI='readFile.xml')
		d.Read()
		f=open('readFile.xml')
		fData=f.read()
		f.close()
		self.assertTrue(str(d)==fData,"XML output: %s"%str(d))
		d=Document(baseURI='ascii.xml')
		d.Read()
		f=open('ascii.xml')
		fData=f.read()
		f.close()
		self.assertTrue(str(d)==fData,"XML output: %s"%str(d))
		
	def testCaseResolveBase(self):
		"""Test the use of ResolveURI and ResolveBase"""
		os.chdir(TEST_DATA_DIR)
		parent=Element(None)
		self.assertTrue(parent.ResolveBase() is None,"No default base")
		child=Element(parent)
		self.assertTrue(child.ResolveBase() is None,"No xml:base by default")
		parent.SetBase('file:///index.xml')
		self.assertTrue(child.ResolveBase()=='file:///index.xml',"No xml:base inheritance")
		# Tests with a document follow....
		furl=str(URIFactory.URLFromPathname(os.path.abspath('base.xml')))
		href=URIFactory.URLFromPathname(os.path.abspath('link.xml'))
		hrefPath=href.absPath
		href=str(href)
		altRef='file:///hello/link.xml'
		d=Document(baseURI='base.xml')
		self.assertTrue(d.GetBase()==furl,"Base not resolved relative to w.d. by constructor")
		d.Read()
		tag=d.root
		self.assertTrue(tag.ResolveBase()==furl,"Root element resolves from document")
		self.assertTrue(str(tag.ResolveURI("link.xml"))==href,"Root element HREF")
		self.assertTrue(str(tag.RelativeURI(href))=='link.xml',"Root element relative")
		#self.assertTrue(tag.RelativeURI(altRef)=='/hello/link.xml','Root element full path relative: %s'%tag.RelativeURI(altRef))
		childTag=tag._children[0]
		self.assertTrue(childTag.ResolveBase()=="file:///hello/base.xml","xml:base overrides in childTag (%s)"%childTag.ResolveBase())
		self.assertTrue(str(childTag.ResolveURI("link.xml"))==altRef,"child element HREF")
		self.assertTrue(str(childTag.RelativeURI(href))=='..'+hrefPath,"child element relative resulting in full path: %s"%childTag.RelativeURI(href))
		self.assertTrue(str(childTag.RelativeURI(altRef))=='link.xml','child element relative')
		# We require this next test to ensure that an href to the current document comes up blank
		# Although this was a major source of bugs in browsers (<img src=''> causing infinite loading loops)
		# these are largely fixed now and obfuscating by using a non-empty relative link to ourselves is
		# likely to start the whole thing going again.
		self.assertTrue(str(childTag.RelativeURI(childTag.ResolveBase()))=='','child element relative avoiding empty URI(%s)'%childTag.RelativeURI(childTag.ResolveBase()))
		grandChildTag=childTag._children[0]
		self.assertTrue(grandChildTag.ResolveBase()=="file:///hello/base.xml","xml:base inherited")
		self.assertTrue(str(grandChildTag.ResolveURI("link.xml"))==altRef,"grandChild element HREF inherited")
		self.assertTrue(str(grandChildTag.RelativeURI(href))=='..'+hrefPath,"grandChild element relative inherited: %s"%grandChildTag.RelativeURI(href))
		self.assertTrue(str(grandChildTag.RelativeURI(altRef))=='link.xml','grandChild element relative inherited')
	
	def testCaseResolveLang(self):
		"""Test the use of ResolveLang"""
		parent=Element(None)
		self.assertTrue(parent.ResolveLang() is None,"No default language")
		parent.SetLang('en-GB')
		self.assertTrue(parent.GetLang()=='en-GB',"Lang Get/Set")
		child=Element(parent)
		self.assertTrue(child.GetLang() is None,"No xml:lang by default")
		self.assertTrue(child.ResolveLang()=='en-GB',"Lang inheritence")
		# repeat tests with a parent document
		d=Document()
		parent=Element(d)
		self.assertTrue(parent.ResolveLang() is None,"No default language")
		
	def testCaseCreate(self):
		"""Test the creating of the Document on the file system"""		
		CREATE_1_XML="""<?xml version="1.0" encoding="UTF-8"?>
<test/>"""
		d=Document(root=NamedElement)
		d.SetBase('create1.xml')
		d.Create()
		try:
			f=open("create1.xml")
			data=f.read()
			f.close()
			self.assertTrue(data==CREATE_1_XML,"Create Test")
		except IOError:
			self.fail("Create Test failed to create file")
	
	def testCaseUpdate(self):
		"""Test the updating of the MXLDocument on the file system"""
		UPDATE_1_XML="""<?xml version="1.0" encoding="UTF-8"?>
<test>
	<test/>
</test>"""
		d=Document(root=NamedElement)
		d.SetBase('update1.xml')
		try:
			d.Update()
			self.fail("Update Document failed to spot missing file")
		except XMLMissingResourceError:
			pass
		d.Create()
		d.root.ChildElement(NamedElement)
		d.Update()
		try:
			f=open("update1.xml")
			data=f.read()
			f.close()
			self.assertTrue(data==UPDATE_1_XML,"Update Test")
		except IOError:
			self.fail("Update Test failed to update file")			
		
	def testCaseID(self):
		"""Test the built-in handling of a document's ID space."""
		doc=Document()
		e1=Element(doc)
		e2=Element(doc)
		e1.id=e2.id='test'
		doc.RegisterElement(e1)
		try:
			doc.RegisterElement(e2)
			self.fail("Failed to spot ID clash")
		except XMLIDClashError:
			pass
		e2.id='test2'
		doc.RegisterElement(e2)
		self.assertTrue(doc.GetElementByID('test') is e1,"Element look-up failed")
		newID=doc.GetUniqueID('test')
		self.assertFalse(newID=='test' or newID=='test2')
	
	def testCaseReflection(self):
		"""Test the built-in handling of reflective attributes and elements."""
		REFLECTIVE_XML="""<?xml version="1.0" encoding="UTF-8"?>
<reflection btest="Hello"><etest>Hello Again</etest></reflection>"""
		f=StringIO(REFLECTIVE_XML)
		d=ReflectiveDocument()
		d.Read(src=f)
		root=d.root
		self.assertTrue(isinstance(root,ReflectiveElement))
		self.assertTrue(root.bTest,"Attribute relfection")
		self.assertTrue(root.child,"Element relfection")
		
		
if __name__ == "__main__":
	logging.basicConfig(level=logging.INFO)
	unittest.main()
