2005-06-08 17:15:58 +00:00
#!/usr/bin/env python
#
# This is the MS subset of the W3C test suite for XML Schemas.
# This file is generated from the MS W3c test suite description file.
#
import sys , os
import exceptions , optparse
import libxml2
opa = optparse . OptionParser ( )
opa . add_option ( " -b " , " --base " , action = " store " , type = " string " , dest = " baseDir " ,
default = " " ,
help = """ The base directory; i.e. the parent folder of the
" nisttest " , " suntest " and " msxsdtest " directories . """ )
opa . add_option ( " -o " , " --out " , action = " store " , type = " string " , dest = " logFile " ,
default = " test.log " ,
help = " The filepath of the log file to be created " )
opa . add_option ( " --log " , action = " store_true " , dest = " enableLog " ,
default = False ,
help = " Create the log file " )
opa . add_option ( " --no-test-out " , action = " store_true " , dest = " disableTestStdOut " ,
default = False ,
help = " Don ' t output test results " )
opa . add_option ( " -s " , " --silent " , action = " store_true " , dest = " silent " , default = False ,
help = " Disables display of all tests " )
opa . add_option ( " -v " , " --verbose " , action = " store_true " , dest = " verbose " ,
default = False ,
help = " Displays all tests (only if --silent is not set) " )
opa . add_option ( " -x " , " --max " , type = " int " , dest = " maxTestCount " ,
default = " -1 " ,
help = " The maximum number of tests to be run " )
opa . add_option ( " -t " , " --test " , type = " string " , dest = " singleTest " ,
default = None ,
help = " Runs the specified test only " )
opa . add_option ( " --tsw " , " --test-starts-with " , type = " string " , dest = " testStartsWith " ,
default = None ,
help = " Runs the specified test(s), starting with the given string " )
opa . add_option ( " --rieo " , " --report-internal-errors-only " , action = " store_true " ,
dest = " reportInternalErrOnly " , default = False ,
help = " Display erroneous tests of type ' internal ' only " )
opa . add_option ( " --rueo " , " --report-unimplemented-errors-only " , action = " store_true " ,
dest = " reportUnimplErrOnly " , default = False ,
help = " Display erroneous tests of type ' unimplemented ' only " )
opa . add_option ( " --rmleo " , " --report-mem-leak-errors-only " , action = " store_true " ,
dest = " reportMemLeakErrOnly " , default = False ,
help = " Display erroneous tests of type ' memory leak ' only " )
opa . add_option ( " -c " , " --combines " , type = " string " , dest = " combines " ,
default = None ,
help = " Combines to be run (all if omitted) " )
opa . add_option ( " --csw " , " --csw " , type = " string " , dest = " combineStartsWith " ,
default = None ,
help = " Combines to be run (all if omitted) " )
opa . add_option ( " --rc " , " --report-combines " , action = " store_true " ,
dest = " reportCombines " , default = False ,
help = " Display combine reports " )
opa . add_option ( " --rec " , " --report-err-combines " , action = " store_true " ,
dest = " reportErrCombines " , default = False ,
help = " Display erroneous combine reports only " )
opa . add_option ( " --debug " , action = " store_true " ,
dest = " debugEnabled " , default = False ,
help = " Displays debug messages " )
opa . add_option ( " --info " , action = " store_true " ,
dest = " info " , default = False ,
help = " Displays info on the suite only. Does not run any test. " )
2005-06-16 12:44:35 +00:00
opa . add_option ( " --sax " , action = " store_true " ,
dest = " validationSAX " , default = False ,
help = " Use SAX2-driven validation. " )
opa . add_option ( " --tn " , action = " store_true " ,
dest = " displayTestName " , default = False ,
help = " Display the test name in every case. " )
2005-06-08 17:15:58 +00:00
( options , args ) = opa . parse_args ( )
if options . combines is not None :
options . combines = options . combines . split ( )
################################################
# The vars below are not intended to be changed.
#
msgSchemaNotValidButShould = " The schema should be valid. "
msgSchemaValidButShouldNot = " The schema should be invalid. "
msgInstanceNotValidButShould = " The instance should be valid. "
msgInstanceValidButShouldNot = " The instance should be invalid. "
vendorNIST = " NIST "
vendorNIST_2 = " NIST-2 "
vendorSUN = " SUN "
vendorMS = " MS "
###################
# Helper functions.
#
vendor = None
def handleError ( test , msg ) :
global options
if not options . silent :
test . addLibLog ( " ' %s ' LIB: %s " % ( test . name , msg ) )
if msg . find ( " Unimplemented " ) > - 1 :
test . failUnimplemented ( )
elif msg . find ( " Internal " ) > - 1 :
test . failInternal ( )
def fixFileNames ( fileName ) :
if ( fileName is None ) or ( fileName == " " ) :
return " "
dirs = fileName . split ( " / " )
if dirs [ 1 ] != " Tests " :
fileName = os . path . join ( " . " , " Tests " )
for dir in dirs [ 1 : ] :
fileName = os . path . join ( fileName , dir )
return fileName
class XSTCTestGroup :
def __init__ ( self , name , schemaFileName , descr ) :
global vendor , vendorNIST_2
self . name = name
self . descr = descr
self . mainSchema = True
self . schemaFileName = fixFileNames ( schemaFileName )
self . schemaParsed = False
self . schemaTried = False
def setSchema ( self , schemaFileName , parsed ) :
if not self . mainSchema :
return
self . mainSchema = False
self . schemaParsed = parsed
self . schemaTried = True
class XSTCTestCase :
# <!-- groupName, Name, Accepted, File, Val, Descr
def __init__ ( self , isSchema , groupName , name , accepted , file , val , descr ) :
global options
#
# Constructor.
#
self . testRunner = None
self . isSchema = isSchema
self . groupName = groupName
self . name = name
self . accepted = accepted
self . fileName = fixFileNames ( file )
self . val = val
self . descr = descr
self . failed = False
self . combineName = None
self . log = [ ]
self . libLog = [ ]
self . initialMemUsed = 0
self . memLeak = 0
self . excepted = False
self . bad = False
self . unimplemented = False
self . internalErr = False
self . noSchemaErr = False
self . failed = False
#
# Init the log.
#
if not options . silent :
if self . descr is not None :
self . log . append ( " ' %s ' descr: %s \n " % ( self . name , self . descr ) )
self . log . append ( " ' %s ' exp validity: %d \n " % ( self . name , self . val ) )
def initTest ( self , runner ) :
global vendorNIST , vendorSUN , vendorMS , vendorNIST_2 , options , vendor
#
# Get the test-group.
#
self . runner = runner
self . group = runner . getGroup ( self . groupName )
if vendor == vendorMS or vendor == vendorSUN :
#
# Use the last given directory for the combine name.
#
dirs = self . fileName . split ( " / " )
self . combineName = dirs [ len ( dirs ) - 2 ]
elif vendor == vendorNIST :
#
# NIST files are named in the following form:
# "NISTSchema-short-pattern-1.xsd"
#
tokens = self . name . split ( " - " )
self . combineName = tokens [ 1 ]
elif vendor == vendorNIST_2 :
#
# Group-names have the form: "atomic-normalizedString-length-1"
#
tokens = self . groupName . split ( " - " )
self . combineName = " %s - %s " % ( tokens [ 0 ] , tokens [ 1 ] )
else :
self . combineName = " unkown "
raise Exception ( " Could not compute the combine name of a test. " )
if ( not options . silent ) and ( self . group . descr is not None ) :
self . log . append ( " ' %s ' group-descr: %s \n " % ( self . name , self . group . descr ) )
def addLibLog ( self , msg ) :
""" This one is intended to be used by the error handler
function """
global options
if not options . silent :
self . libLog . append ( msg )
def fail ( self , msg ) :
global options
self . failed = True
if not options . silent :
self . log . append ( " ' %s ' ( FAILED: %s \n " % ( self . name , msg ) )
def failNoSchema ( self ) :
global options
self . failed = True
self . noSchemaErr = True
if not options . silent :
self . log . append ( " ' %s ' X NO-SCHEMA \n " % ( self . name ) )
def failInternal ( self ) :
global options
self . failed = True
self . internalErr = True
if not options . silent :
self . log . append ( " ' %s ' * INTERNAL \n " % self . name )
def failUnimplemented ( self ) :
global options
self . failed = True
self . unimplemented = True
if not options . silent :
self . log . append ( " ' %s ' ? UNIMPLEMENTED \n " % self . name )
def failCritical ( self , msg ) :
global options
self . failed = True
self . bad = True
if not options . silent :
self . log . append ( " ' %s ' ! BAD: %s \n " % ( self . name , msg ) )
def failExcept ( self , e ) :
global options
self . failed = True
self . excepted = True
if not options . silent :
self . log . append ( " ' %s ' # EXCEPTION: %s \n " % ( self . name , e . __str__ ( ) ) )
def setUp ( self ) :
#
# Set up Libxml2.
#
self . initialMemUsed = libxml2 . debugMemory ( 1 )
libxml2 . initParser ( )
libxml2 . lineNumbersDefault ( 1 )
libxml2 . registerErrorHandler ( handleError , self )
def tearDown ( self ) :
libxml2 . schemaCleanupTypes ( )
libxml2 . cleanupParser ( )
self . memLeak = libxml2 . debugMemory ( 1 ) - self . initialMemUsed
def isIOError ( self , file , docType ) :
err = None
try :
err = libxml2 . lastError ( )
except :
# Suppress exceptions.
pass
if ( err is None ) :
return False
if err . domain ( ) == libxml2 . XML_FROM_IO :
self . failCritical ( " failed to access the %s resource ' %s ' \n " % ( docType , file ) )
def debugMsg ( self , msg ) :
global options
if options . debugEnabled :
sys . stdout . write ( " ' %s ' DEBUG: %s \n " % ( self . name , msg ) )
def finalize ( self ) :
global options
""" Adds additional info to the log. """
#
# Add libxml2 messages.
#
if not options . silent :
self . log . extend ( self . libLog )
#
# Add memory leaks.
#
if self . memLeak != 0 :
self . log . append ( " %s + memory leak: %d bytes \n " % ( self . name , self . memLeak ) )
def run ( self ) :
""" Runs a test. """
global options
##filePath = os.path.join(options.baseDir, self.fileName)
# filePath = "%s/%s/%s/%s" % (options.baseDir, self.test_Folder, self.schema_Folder, self.schema_File)
2005-06-16 12:44:35 +00:00
if options . displayTestName :
sys . stdout . write ( " ' %s ' \n " % self . name )
2005-06-08 17:15:58 +00:00
try :
self . validate ( )
except ( Exception , libxml2 . parserError , libxml2 . treeError ) , e :
self . failExcept ( e )
def parseSchema ( fileName ) :
schema = None
ctxt = libxml2 . schemaNewParserCtxt ( fileName )
try :
try :
schema = ctxt . schemaParse ( )
except :
pass
finally :
del ctxt
return schema
class XSTCSchemaTest ( XSTCTestCase ) :
def __init__ ( self , groupName , name , accepted , file , val , descr ) :
XSTCTestCase . __init__ ( self , 1 , groupName , name , accepted , file , val , descr )
def validate ( self ) :
global msgSchemaNotValidButShould , msgSchemaValidButShouldNot
schema = None
filePath = self . fileName
# os.path.join(options.baseDir, self.fileName)
valid = 0
try :
#
# Parse the schema.
#
self . debugMsg ( " loading schema: %s " % filePath )
schema = parseSchema ( filePath )
self . debugMsg ( " after loading schema " )
if schema is None :
self . debugMsg ( " schema is None " )
self . debugMsg ( " checking for IO errors... " )
if self . isIOError ( file , " schema " ) :
return
self . debugMsg ( " checking schema result " )
if ( schema is None and self . val ) or ( schema is not None and self . val == 0 ) :
self . debugMsg ( " schema result is BAD " )
if ( schema == None ) :
self . fail ( msgSchemaNotValidButShould )
else :
self . fail ( msgSchemaValidButShouldNot )
else :
self . debugMsg ( " schema result is OK " )
finally :
self . group . setSchema ( self . fileName , schema is not None )
del schema
class XSTCInstanceTest ( XSTCTestCase ) :
def __init__ ( self , groupName , name , accepted , file , val , descr ) :
XSTCTestCase . __init__ ( self , 0 , groupName , name , accepted , file , val , descr )
def validate ( self ) :
instance = None
schema = None
filePath = self . fileName
# os.path.join(options.baseDir, self.fileName)
if not self . group . schemaParsed and self . group . schemaTried :
self . failNoSchema ( )
return
self . debugMsg ( " loading instance: %s " % filePath )
parserCtxt = libxml2 . newParserCtxt ( )
if ( parserCtxt is None ) :
# TODO: Is this one necessary, or will an exception
# be already raised?
raise Exception ( " Could not create the instance parser context. " )
2005-06-16 12:44:35 +00:00
if not options . validationSAX :
2005-06-08 17:15:58 +00:00
try :
2005-06-16 12:44:35 +00:00
try :
instance = parserCtxt . ctxtReadFile ( filePath , None , libxml2 . XML_PARSE_NOWARNING )
except :
# Suppress exceptions.
pass
finally :
del parserCtxt
self . debugMsg ( " after loading instance " )
if instance is None :
self . debugMsg ( " instance is None " )
self . failCritical ( " Failed to parse the instance for unknown reasons. " )
return
try :
#
# Validate the instance.
#
self . debugMsg ( " loading schema: %s " % self . group . schemaFileName )
schema = parseSchema ( self . group . schemaFileName )
2005-06-08 17:15:58 +00:00
try :
2005-06-16 12:44:35 +00:00
validationCtxt = schema . schemaNewValidCtxt ( )
#validationCtxt = libxml2.schemaNewValidCtxt(None)
if ( validationCtxt is None ) :
self . failCritical ( " Could not create the validation context. " )
return
2005-06-08 17:15:58 +00:00
try :
2005-06-16 12:44:35 +00:00
self . debugMsg ( " validating instance " )
if options . validationSAX :
instance_Err = validationCtxt . schemaValidateFile ( filePath , 0 )
else :
2005-06-08 17:15:58 +00:00
instance_Err = validationCtxt . schemaValidateDoc ( instance )
2005-06-16 12:44:35 +00:00
self . debugMsg ( " after instance validation " )
self . debugMsg ( " instance-err: %d " % instance_Err )
if ( instance_Err != 0 and self . val == 1 ) or ( instance_Err == 0 and self . val == 0 ) :
self . debugMsg ( " instance result is BAD " )
if ( instance_Err != 0 ) :
self . fail ( msgInstanceNotValidButShould )
2005-06-08 17:15:58 +00:00
else :
2005-06-16 12:44:35 +00:00
self . fail ( msgInstanceValidButShouldNot )
else :
self . debugMsg ( " instance result is OK " )
2005-06-08 17:15:58 +00:00
finally :
2005-06-16 12:44:35 +00:00
del validationCtxt
2005-06-08 17:15:58 +00:00
finally :
2005-06-16 12:44:35 +00:00
del schema
finally :
if instance is not None :
2005-06-08 17:15:58 +00:00
instance . freeDoc ( )
####################
# Test runner class.
#
class XSTCTestRunner :
CNT_TOTAL = 0
CNT_RAN = 1
CNT_SUCCEEDED = 2
CNT_FAILED = 3
CNT_UNIMPLEMENTED = 4
CNT_INTERNAL = 5
CNT_BAD = 6
CNT_EXCEPTED = 7
CNT_MEMLEAK = 8
CNT_NOSCHEMA = 9
CNT_NOTACCEPTED = 10
CNT_SCHEMA_TEST = 11
def __init__ ( self ) :
self . logFile = None
self . counters = self . createCounters ( )
self . testList = [ ]
self . combinesRan = { }
self . groups = { }
self . curGroup = None
def createCounters ( self ) :
counters = { self . CNT_TOTAL : 0 , self . CNT_RAN : 0 , self . CNT_SUCCEEDED : 0 ,
self . CNT_FAILED : 0 , self . CNT_UNIMPLEMENTED : 0 , self . CNT_INTERNAL : 0 , self . CNT_BAD : 0 ,
self . CNT_EXCEPTED : 0 , self . CNT_MEMLEAK : 0 , self . CNT_NOSCHEMA : 0 , self . CNT_NOTACCEPTED : 0 ,
self . CNT_SCHEMA_TEST : 0 }
return counters
def addTest ( self , test ) :
self . testList . append ( test )
test . initTest ( self )
def getGroup ( self , groupName ) :
return self . groups [ groupName ]
def addGroup ( self , group ) :
self . groups [ group . name ] = group
def updateCounters ( self , test , counters ) :
if test . memLeak != 0 :
counters [ self . CNT_MEMLEAK ] + = 1
if not test . failed :
counters [ self . CNT_SUCCEEDED ] + = 1
if test . failed :
counters [ self . CNT_FAILED ] + = 1
if test . bad :
counters [ self . CNT_BAD ] + = 1
if test . unimplemented :
counters [ self . CNT_UNIMPLEMENTED ] + = 1
if test . internalErr :
counters [ self . CNT_INTERNAL ] + = 1
if test . noSchemaErr :
counters [ self . CNT_NOSCHEMA ] + = 1
if test . excepted :
counters [ self . CNT_EXCEPTED ] + = 1
if not test . accepted :
counters [ self . CNT_NOTACCEPTED ] + = 1
if test . isSchema :
counters [ self . CNT_SCHEMA_TEST ] + = 1
return counters
def displayResults ( self , out , all , combName , counters ) :
out . write ( " \n " )
if all :
if options . combines is not None :
out . write ( " combine(s): %s \n " % str ( options . combines ) )
elif combName is not None :
out . write ( " combine : %s \n " % combName )
out . write ( " total : %d \n " % counters [ self . CNT_TOTAL ] )
if all or options . combines is not None :
out . write ( " ran : %d \n " % counters [ self . CNT_RAN ] )
out . write ( " (schemata) : %d \n " % counters [ self . CNT_SCHEMA_TEST ] )
# out.write(" succeeded : %d\n" % counters[self.CNT_SUCCEEDED])
out . write ( " not accepted : %d \n " % counters [ self . CNT_NOTACCEPTED ] )
if counters [ self . CNT_FAILED ] > 0 :
out . write ( " failed : %d \n " % counters [ self . CNT_FAILED ] )
out . write ( " -> internal : %d \n " % counters [ self . CNT_INTERNAL ] )
out . write ( " -> unimpl. : %d \n " % counters [ self . CNT_UNIMPLEMENTED ] )
out . write ( " -> skip-invalid-schema : %d \n " % counters [ self . CNT_NOSCHEMA ] )
out . write ( " -> bad : %d \n " % counters [ self . CNT_BAD ] )
out . write ( " -> exceptions : %d \n " % counters [ self . CNT_EXCEPTED ] )
out . write ( " memory leaks : %d \n " % counters [ self . CNT_MEMLEAK ] )
def displayShortResults ( self , out , all , combName , counters ) :
out . write ( " Ran %d of %d tests ( %d schemata): " % ( counters [ self . CNT_RAN ] ,
counters [ self . CNT_TOTAL ] , counters [ self . CNT_SCHEMA_TEST ] ) )
# out.write(" succeeded : %d\n" % counters[self.CNT_SUCCEEDED])
if counters [ self . CNT_NOTACCEPTED ] > 0 :
out . write ( " %d not accepted " % ( counters [ self . CNT_NOTACCEPTED ] ) )
if counters [ self . CNT_FAILED ] > 0 or counters [ self . CNT_MEMLEAK ] > 0 :
if counters [ self . CNT_FAILED ] > 0 :
out . write ( " %d failed " % ( counters [ self . CNT_FAILED ] ) )
out . write ( " ( " )
if counters [ self . CNT_INTERNAL ] > 0 :
out . write ( " %d internal " % ( counters [ self . CNT_INTERNAL ] ) )
if counters [ self . CNT_UNIMPLEMENTED ] > 0 :
out . write ( " %d unimplemented " % ( counters [ self . CNT_UNIMPLEMENTED ] ) )
if counters [ self . CNT_NOSCHEMA ] > 0 :
out . write ( " %d skip-invalid-schema " % ( counters [ self . CNT_NOSCHEMA ] ) )
if counters [ self . CNT_BAD ] > 0 :
out . write ( " %d bad " % ( counters [ self . CNT_BAD ] ) )
if counters [ self . CNT_EXCEPTED ] > 0 :
out . write ( " %d exception " % ( counters [ self . CNT_EXCEPTED ] ) )
out . write ( " ) " )
if counters [ self . CNT_MEMLEAK ] > 0 :
out . write ( " %d leaks " % ( counters [ self . CNT_MEMLEAK ] ) )
out . write ( " \n " )
else :
out . write ( " all passed \n " )
def reportCombine ( self , combName ) :
global options
counters = self . createCounters ( )
#
# Compute evaluation counters.
#
for test in self . combinesRan [ combName ] :
counters [ self . CNT_TOTAL ] + = 1
counters [ self . CNT_RAN ] + = 1
counters = self . updateCounters ( test , counters )
if options . reportErrCombines and ( counters [ self . CNT_FAILED ] == 0 ) and ( counters [ self . CNT_MEMLEAK ] == 0 ) :
pass
else :
if options . enableLog :
self . displayResults ( self . logFile , False , combName , counters )
self . displayResults ( sys . stdout , False , combName , counters )
def displayTestLog ( self , test ) :
sys . stdout . writelines ( test . log )
sys . stdout . write ( " ~~~~~~~~~~ \n " )
def reportTest ( self , test ) :
global options
error = test . failed or test . memLeak != 0
#
# Only erroneous tests will be written to the log,
# except @verbose is switched on.
#
if options . enableLog and ( options . verbose or error ) :
self . logFile . writelines ( test . log )
self . logFile . write ( " ~~~~~~~~~~ \n " )
#
# if not @silent, only erroneous tests will be
# written to stdout, except @verbose is switched on.
#
if not options . silent :
if options . reportInternalErrOnly and test . internalErr :
self . displayTestLog ( test )
if options . reportMemLeakErrOnly and test . memLeak != 0 :
self . displayTestLog ( test )
if options . reportUnimplErrOnly and test . unimplemented :
self . displayTestLog ( test )
if ( options . verbose or error ) and ( not options . reportInternalErrOnly ) and ( not options . reportMemLeakErrOnly ) and ( not options . reportUnimplErrOnly ) :
self . displayTestLog ( test )
def addToCombines ( self , test ) :
found = False
if self . combinesRan . has_key ( test . combineName ) :
self . combinesRan [ test . combineName ] . append ( test )
else :
self . combinesRan [ test . combineName ] = [ test ]
def run ( self ) :
global options
if options . info :
for test in self . testList :
self . addToCombines ( test )
sys . stdout . write ( " Combines: %d \n " % len ( self . combinesRan ) )
sys . stdout . write ( " %s \n " % self . combinesRan . keys ( ) )
return
if options . enableLog :
self . logFile = open ( options . logFile , " w " )
try :
for test in self . testList :
self . counters [ self . CNT_TOTAL ] + = 1
#
# Filter tests.
#
if options . singleTest is not None and options . singleTest != " " :
if ( test . name != options . singleTest ) :
continue
elif options . combines is not None :
if not options . combines . __contains__ ( test . combineName ) :
continue
elif options . testStartsWith is not None :
if not test . name . startswith ( options . testStartsWith ) :
continue
elif options . combineStartsWith is not None :
if not test . combineName . startswith ( options . combineStartsWith ) :
continue
if options . maxTestCount != - 1 and self . counters [ self . CNT_RAN ] > = options . maxTestCount :
break
self . counters [ self . CNT_RAN ] + = 1
#
# Run the thing, dammit.
#
try :
test . setUp ( )
try :
test . run ( )
finally :
test . tearDown ( )
finally :
#
# Evaluate.
#
test . finalize ( )
self . reportTest ( test )
if options . reportCombines or options . reportErrCombines :
self . addToCombines ( test )
self . counters = self . updateCounters ( test , self . counters )
finally :
if options . reportCombines or options . reportErrCombines :
#
# Build a report for every single combine.
#
# TODO: How to sort a dict?
#
self . combinesRan . keys ( ) . sort ( None )
for key in self . combinesRan . keys ( ) :
self . reportCombine ( key )
#
# Display the final report.
#
if options . silent :
self . displayShortResults ( sys . stdout , True , None , self . counters )
else :
sys . stdout . write ( " =========================== \n " )
self . displayResults ( sys . stdout , True , None , self . counters )