4 # This is the MS subset of the W3C test suite for XML Schemas.
5 # This file is generated from the MS W3c test suite description file.
9 import exceptions
, optparse
12 opa
= optparse
.OptionParser()
14 opa
.add_option("-b", "--base", action
="store", type="string", dest
="baseDir",
16 help="""The base directory; i.e. the parent folder of the
17 "nisttest", "suntest" and "msxsdtest" directories.""")
19 opa
.add_option("-o", "--out", action
="store", type="string", dest
="logFile",
21 help="The filepath of the log file to be created")
23 opa
.add_option("--log", action
="store_true", dest
="enableLog",
25 help="Create the log file")
27 opa
.add_option("--no-test-out", action
="store_true", dest
="disableTestStdOut",
29 help="Don't output test results")
31 opa
.add_option("-s", "--silent", action
="store_true", dest
="silent", default
=False,
32 help="Disables display of all tests")
34 opa
.add_option("-v", "--verbose", action
="store_true", dest
="verbose",
36 help="Displays all tests (only if --silent is not set)")
38 opa
.add_option("-x", "--max", type="int", dest
="maxTestCount",
40 help="The maximum number of tests to be run")
42 opa
.add_option("-t", "--test", type="string", dest
="singleTest",
44 help="Runs the specified test only")
46 opa
.add_option("--tsw", "--test-starts-with", type="string", dest
="testStartsWith",
48 help="Runs the specified test(s), starting with the given string")
50 opa
.add_option("--rieo", "--report-internal-errors-only", action
="store_true",
51 dest
="reportInternalErrOnly", default
=False,
52 help="Display erroneous tests of type 'internal' only")
54 opa
.add_option("--rueo", "--report-unimplemented-errors-only", action
="store_true",
55 dest
="reportUnimplErrOnly", default
=False,
56 help="Display erroneous tests of type 'unimplemented' only")
58 opa
.add_option("--rmleo", "--report-mem-leak-errors-only", action
="store_true",
59 dest
="reportMemLeakErrOnly", default
=False,
60 help="Display erroneous tests of type 'memory leak' only")
62 opa
.add_option("-c", "--combines", type="string", dest
="combines",
64 help="Combines to be run (all if omitted)")
66 opa
.add_option("--csw", "--csw", type="string", dest
="combineStartsWith",
68 help="Combines to be run (all if omitted)")
70 opa
.add_option("--rc", "--report-combines", action
="store_true",
71 dest
="reportCombines", default
=False,
72 help="Display combine reports")
74 opa
.add_option("--rec", "--report-err-combines", action
="store_true",
75 dest
="reportErrCombines", default
=False,
76 help="Display erroneous combine reports only")
78 opa
.add_option("--debug", action
="store_true",
79 dest
="debugEnabled", default
=False,
80 help="Displays debug messages")
82 opa
.add_option("--info", action
="store_true",
83 dest
="info", default
=False,
84 help="Displays info on the suite only. Does not run any test.")
85 opa
.add_option("--sax", action
="store_true",
86 dest
="validationSAX", default
=False,
87 help="Use SAX2-driven validation.")
88 opa
.add_option("--tn", action
="store_true",
89 dest
="displayTestName", default
=False,
90 help="Display the test name in every case.")
92 (options
, args
) = opa
.parse_args()
94 if options
.combines
is not None:
95 options
.combines
= options
.combines
.split()
97 ################################################
98 # The vars below are not intended to be changed.
101 msgSchemaNotValidButShould
= "The schema should be valid."
102 msgSchemaValidButShouldNot
= "The schema should be invalid."
103 msgInstanceNotValidButShould
= "The instance should be valid."
104 msgInstanceValidButShouldNot
= "The instance should be invalid."
106 vendorNIST_2
= "NIST-2"
115 def handleError(test
, msg
):
117 if not options
.silent
:
118 test
.addLibLog("'%s' LIB: %s" % (test
.name
, msg
))
119 if msg
.find("Unimplemented") > -1:
120 test
.failUnimplemented()
121 elif msg
.find("Internal") > -1:
125 def fixFileNames(fileName
):
126 if (fileName
is None) or (fileName
== ""):
128 dirs
= fileName
.split("/")
129 if dirs
[1] != "Tests":
130 fileName
= os
.path
.join(".", "Tests")
132 fileName
= os
.path
.join(fileName
, dir)
136 def __init__(self
, name
, schemaFileName
, descr
):
137 global vendor
, vendorNIST_2
140 self
.mainSchema
= True
141 self
.schemaFileName
= fixFileNames(schemaFileName
)
142 self
.schemaParsed
= False
143 self
.schemaTried
= False
145 def setSchema(self
, schemaFileName
, parsed
):
146 if not self
.mainSchema
:
148 self
.mainSchema
= False
149 self
.schemaParsed
= parsed
150 self
.schemaTried
= True
154 # <!-- groupName, Name, Accepted, File, Val, Descr
155 def __init__(self
, isSchema
, groupName
, name
, accepted
, file, val
, descr
):
160 self
.testRunner
= None
161 self
.isSchema
= isSchema
162 self
.groupName
= groupName
164 self
.accepted
= accepted
165 self
.fileName
= fixFileNames(file)
169 self
.combineName
= None
173 self
.initialMemUsed
= 0
175 self
.excepted
= False
177 self
.unimplemented
= False
178 self
.internalErr
= False
179 self
.noSchemaErr
= False
184 if not options
.silent
:
185 if self
.descr
is not None:
186 self
.log
.append("'%s' descr: %s\n" % (self
.name
, self
.descr
))
187 self
.log
.append("'%s' exp validity: %d\n" % (self
.name
, self
.val
))
189 def initTest(self
, runner
):
190 global vendorNIST
, vendorSUN
, vendorMS
, vendorNIST_2
, options
, vendor
192 # Get the test-group.
195 self
.group
= runner
.getGroup(self
.groupName
)
196 if vendor
== vendorMS
or vendor
== vendorSUN
:
198 # Use the last given directory for the combine name.
200 dirs
= self
.fileName
.split("/")
201 self
.combineName
= dirs
[len(dirs
) -2]
202 elif vendor
== vendorNIST
:
204 # NIST files are named in the following form:
205 # "NISTSchema-short-pattern-1.xsd"
207 tokens
= self
.name
.split("-")
208 self
.combineName
= tokens
[1]
209 elif vendor
== vendorNIST_2
:
211 # Group-names have the form: "atomic-normalizedString-length-1"
213 tokens
= self
.groupName
.split("-")
214 self
.combineName
= "%s-%s" % (tokens
[0], tokens
[1])
216 self
.combineName
= "unkown"
217 raise Exception("Could not compute the combine name of a test.")
218 if (not options
.silent
) and (self
.group
.descr
is not None):
219 self
.log
.append("'%s' group-descr: %s\n" % (self
.name
, self
.group
.descr
))
222 def addLibLog(self
, msg
):
223 """This one is intended to be used by the error handler
226 if not options
.silent
:
227 self
.libLog
.append(msg
)
232 if not options
.silent
:
233 self
.log
.append("'%s' ( FAILED: %s\n" % (self
.name
, msg
))
235 def failNoSchema(self
):
238 self
.noSchemaErr
= True
239 if not options
.silent
:
240 self
.log
.append("'%s' X NO-SCHEMA\n" % (self
.name
))
242 def failInternal(self
):
245 self
.internalErr
= True
246 if not options
.silent
:
247 self
.log
.append("'%s' * INTERNAL\n" % self
.name
)
249 def failUnimplemented(self
):
252 self
.unimplemented
= True
253 if not options
.silent
:
254 self
.log
.append("'%s' ? UNIMPLEMENTED\n" % self
.name
)
256 def failCritical(self
, msg
):
260 if not options
.silent
:
261 self
.log
.append("'%s' ! BAD: %s\n" % (self
.name
, msg
))
263 def failExcept(self
, e
):
267 if not options
.silent
:
268 self
.log
.append("'%s' # EXCEPTION: %s\n" % (self
.name
, e
.__str
__()))
274 self
.initialMemUsed
= libxml2
.debugMemory(1)
276 libxml2
.lineNumbersDefault(1)
277 libxml2
.registerErrorHandler(handleError
, self
)
280 libxml2
.schemaCleanupTypes()
281 libxml2
.cleanupParser()
282 self
.memLeak
= libxml2
.debugMemory(1) - self
.initialMemUsed
284 def isIOError(self
, file, docType
):
287 err
= libxml2
.lastError()
289 # Suppress exceptions.
293 if err
.domain() == libxml2
.XML_FROM_IO
:
294 self
.failCritical("failed to access the %s resource '%s'\n" % (docType
, file))
296 def debugMsg(self
, msg
):
298 if options
.debugEnabled
:
299 sys
.stdout
.write("'%s' DEBUG: %s\n" % (self
.name
, msg
))
303 """Adds additional info to the log."""
305 # Add libxml2 messages.
307 if not options
.silent
:
308 self
.log
.extend(self
.libLog
)
312 if self
.memLeak
!= 0:
313 self
.log
.append("%s + memory leak: %d bytes\n" % (self
.name
, self
.memLeak
))
319 ##filePath = os.path.join(options.baseDir, self.fileName)
320 # filePath = "%s/%s/%s/%s" % (options.baseDir, self.test_Folder, self.schema_Folder, self.schema_File)
321 if options
.displayTestName
:
322 sys
.stdout
.write("'%s'\n" % self
.name
)
325 except (Exception, libxml2
.parserError
, libxml2
.treeError
), e
:
328 def parseSchema(fileName
):
330 ctxt
= libxml2
.schemaNewParserCtxt(fileName
)
333 schema
= ctxt
.schemaParse()
341 class XSTCSchemaTest(XSTCTestCase
):
343 def __init__(self
, groupName
, name
, accepted
, file, val
, descr
):
344 XSTCTestCase
.__init
__(self
, 1, groupName
, name
, accepted
, file, val
, descr
)
347 global msgSchemaNotValidButShould
, msgSchemaValidButShouldNot
349 filePath
= self
.fileName
350 # os.path.join(options.baseDir, self.fileName)
356 self
.debugMsg("loading schema: %s" % filePath
)
357 schema
= parseSchema(filePath
)
358 self
.debugMsg("after loading schema")
360 self
.debugMsg("schema is None")
361 self
.debugMsg("checking for IO errors...")
362 if self
.isIOError(file, "schema"):
364 self
.debugMsg("checking schema result")
365 if (schema
is None and self
.val
) or (schema
is not None and self
.val
== 0):
366 self
.debugMsg("schema result is BAD")
368 self
.fail(msgSchemaNotValidButShould
)
370 self
.fail(msgSchemaValidButShouldNot
)
372 self
.debugMsg("schema result is OK")
374 self
.group
.setSchema(self
.fileName
, schema
is not None)
377 class XSTCInstanceTest(XSTCTestCase
):
379 def __init__(self
, groupName
, name
, accepted
, file, val
, descr
):
380 XSTCTestCase
.__init
__(self
, 0, groupName
, name
, accepted
, file, val
, descr
)
385 filePath
= self
.fileName
386 # os.path.join(options.baseDir, self.fileName)
388 if not self
.group
.schemaParsed
and self
.group
.schemaTried
:
392 self
.debugMsg("loading instance: %s" % filePath
)
393 parserCtxt
= libxml2
.newParserCtxt()
394 if (parserCtxt
is None):
395 # TODO: Is this one necessary, or will an exception
397 raise Exception("Could not create the instance parser context.")
398 if not options
.validationSAX
:
401 instance
= parserCtxt
.ctxtReadFile(filePath
, None, libxml2
.XML_PARSE_NOWARNING
)
403 # Suppress exceptions.
407 self
.debugMsg("after loading instance")
409 self
.debugMsg("instance is None")
410 self
.failCritical("Failed to parse the instance for unknown reasons.")
414 # Validate the instance.
416 self
.debugMsg("loading schema: %s" % self
.group
.schemaFileName
)
417 schema
= parseSchema(self
.group
.schemaFileName
)
419 validationCtxt
= schema
.schemaNewValidCtxt()
420 #validationCtxt = libxml2.schemaNewValidCtxt(None)
421 if (validationCtxt
is None):
422 self
.failCritical("Could not create the validation context.")
425 self
.debugMsg("validating instance")
426 if options
.validationSAX
:
427 instance_Err
= validationCtxt
.schemaValidateFile(filePath
, 0)
429 instance_Err
= validationCtxt
.schemaValidateDoc(instance
)
430 self
.debugMsg("after instance validation")
431 self
.debugMsg("instance-err: %d" % instance_Err
)
432 if (instance_Err
!= 0 and self
.val
== 1) or (instance_Err
== 0 and self
.val
== 0):
433 self
.debugMsg("instance result is BAD")
434 if (instance_Err
!= 0):
435 self
.fail(msgInstanceNotValidButShould
)
437 self
.fail(msgInstanceValidButShouldNot
)
440 self
.debugMsg("instance result is OK")
446 if instance
is not None:
454 class XSTCTestRunner
:
460 CNT_UNIMPLEMENTED
= 4
471 self
.counters
= self
.createCounters()
473 self
.combinesRan
= {}
477 def createCounters(self
):
478 counters
= {self
.CNT_TOTAL
:0, self
.CNT_RAN
:0, self
.CNT_SUCCEEDED
:0,
479 self
.CNT_FAILED
:0, self
.CNT_UNIMPLEMENTED
:0, self
.CNT_INTERNAL
:0, self
.CNT_BAD
:0,
480 self
.CNT_EXCEPTED
:0, self
.CNT_MEMLEAK
:0, self
.CNT_NOSCHEMA
:0, self
.CNT_NOTACCEPTED
:0,
481 self
.CNT_SCHEMA_TEST
:0}
485 def addTest(self
, test
):
486 self
.testList
.append(test
)
489 def getGroup(self
, groupName
):
490 return self
.groups
[groupName
]
492 def addGroup(self
, group
):
493 self
.groups
[group
.name
] = group
495 def updateCounters(self
, test
, counters
):
496 if test
.memLeak
!= 0:
497 counters
[self
.CNT_MEMLEAK
] += 1
499 counters
[self
.CNT_SUCCEEDED
] +=1
501 counters
[self
.CNT_FAILED
] += 1
503 counters
[self
.CNT_BAD
] += 1
504 if test
.unimplemented
:
505 counters
[self
.CNT_UNIMPLEMENTED
] += 1
507 counters
[self
.CNT_INTERNAL
] += 1
509 counters
[self
.CNT_NOSCHEMA
] += 1
511 counters
[self
.CNT_EXCEPTED
] += 1
512 if not test
.accepted
:
513 counters
[self
.CNT_NOTACCEPTED
] += 1
515 counters
[self
.CNT_SCHEMA_TEST
] += 1
518 def displayResults(self
, out
, all
, combName
, counters
):
521 if options
.combines
is not None:
522 out
.write("combine(s): %s\n" % str(options
.combines
))
523 elif combName
is not None:
524 out
.write("combine : %s\n" % combName
)
525 out
.write(" total : %d\n" % counters
[self
.CNT_TOTAL
])
526 if all
or options
.combines
is not None:
527 out
.write(" ran : %d\n" % counters
[self
.CNT_RAN
])
528 out
.write(" (schemata) : %d\n" % counters
[self
.CNT_SCHEMA_TEST
])
529 # out.write(" succeeded : %d\n" % counters[self.CNT_SUCCEEDED])
530 out
.write(" not accepted : %d\n" % counters
[self
.CNT_NOTACCEPTED
])
531 if counters
[self
.CNT_FAILED
] > 0:
532 out
.write(" failed : %d\n" % counters
[self
.CNT_FAILED
])
533 out
.write(" -> internal : %d\n" % counters
[self
.CNT_INTERNAL
])
534 out
.write(" -> unimpl. : %d\n" % counters
[self
.CNT_UNIMPLEMENTED
])
535 out
.write(" -> skip-invalid-schema : %d\n" % counters
[self
.CNT_NOSCHEMA
])
536 out
.write(" -> bad : %d\n" % counters
[self
.CNT_BAD
])
537 out
.write(" -> exceptions : %d\n" % counters
[self
.CNT_EXCEPTED
])
538 out
.write(" memory leaks : %d\n" % counters
[self
.CNT_MEMLEAK
])
540 def displayShortResults(self
, out
, all
, combName
, counters
):
541 out
.write("Ran %d of %d tests (%d schemata):" % (counters
[self
.CNT_RAN
],
542 counters
[self
.CNT_TOTAL
], counters
[self
.CNT_SCHEMA_TEST
]))
543 # out.write(" succeeded : %d\n" % counters[self.CNT_SUCCEEDED])
544 if counters
[self
.CNT_NOTACCEPTED
] > 0:
545 out
.write(" %d not accepted" % (counters
[self
.CNT_NOTACCEPTED
]))
546 if counters
[self
.CNT_FAILED
] > 0 or counters
[self
.CNT_MEMLEAK
] > 0:
547 if counters
[self
.CNT_FAILED
] > 0:
548 out
.write(" %d failed" % (counters
[self
.CNT_FAILED
]))
550 if counters
[self
.CNT_INTERNAL
] > 0:
551 out
.write(" %d internal" % (counters
[self
.CNT_INTERNAL
]))
552 if counters
[self
.CNT_UNIMPLEMENTED
] > 0:
553 out
.write(" %d unimplemented" % (counters
[self
.CNT_UNIMPLEMENTED
]))
554 if counters
[self
.CNT_NOSCHEMA
] > 0:
555 out
.write(" %d skip-invalid-schema" % (counters
[self
.CNT_NOSCHEMA
]))
556 if counters
[self
.CNT_BAD
] > 0:
557 out
.write(" %d bad" % (counters
[self
.CNT_BAD
]))
558 if counters
[self
.CNT_EXCEPTED
] > 0:
559 out
.write(" %d exception" % (counters
[self
.CNT_EXCEPTED
]))
561 if counters
[self
.CNT_MEMLEAK
] > 0:
562 out
.write(" %d leaks" % (counters
[self
.CNT_MEMLEAK
]))
565 out
.write(" all passed\n")
567 def reportCombine(self
, combName
):
570 counters
= self
.createCounters()
572 # Compute evaluation counters.
574 for test
in self
.combinesRan
[combName
]:
575 counters
[self
.CNT_TOTAL
] += 1
576 counters
[self
.CNT_RAN
] += 1
577 counters
= self
.updateCounters(test
, counters
)
578 if options
.reportErrCombines
and (counters
[self
.CNT_FAILED
] == 0) and (counters
[self
.CNT_MEMLEAK
] == 0):
581 if options
.enableLog
:
582 self
.displayResults(self
.logFile
, False, combName
, counters
)
583 self
.displayResults(sys
.stdout
, False, combName
, counters
)
585 def displayTestLog(self
, test
):
586 sys
.stdout
.writelines(test
.log
)
587 sys
.stdout
.write("~~~~~~~~~~\n")
589 def reportTest(self
, test
):
592 error
= test
.failed
or test
.memLeak
!= 0
594 # Only erroneous tests will be written to the log,
595 # except @verbose is switched on.
597 if options
.enableLog
and (options
.verbose
or error
):
598 self
.logFile
.writelines(test
.log
)
599 self
.logFile
.write("~~~~~~~~~~\n")
601 # if not @silent, only erroneous tests will be
602 # written to stdout, except @verbose is switched on.
604 if not options
.silent
:
605 if options
.reportInternalErrOnly
and test
.internalErr
:
606 self
.displayTestLog(test
)
607 if options
.reportMemLeakErrOnly
and test
.memLeak
!= 0:
608 self
.displayTestLog(test
)
609 if options
.reportUnimplErrOnly
and test
.unimplemented
:
610 self
.displayTestLog(test
)
611 if (options
.verbose
or error
) and (not options
.reportInternalErrOnly
) and (not options
.reportMemLeakErrOnly
) and (not options
.reportUnimplErrOnly
):
612 self
.displayTestLog(test
)
615 def addToCombines(self
, test
):
617 if self
.combinesRan
.has_key(test
.combineName
):
618 self
.combinesRan
[test
.combineName
].append(test
)
620 self
.combinesRan
[test
.combineName
] = [test
]
627 for test
in self
.testList
:
628 self
.addToCombines(test
)
629 sys
.stdout
.write("Combines: %d\n" % len(self
.combinesRan
))
630 sys
.stdout
.write("%s\n" % self
.combinesRan
.keys())
633 if options
.enableLog
:
634 self
.logFile
= open(options
.logFile
, "w")
636 for test
in self
.testList
:
637 self
.counters
[self
.CNT_TOTAL
] += 1
641 if options
.singleTest
is not None and options
.singleTest
!= "":
642 if (test
.name
!= options
.singleTest
):
644 elif options
.combines
is not None:
645 if not options
.combines
.__contains
__(test
.combineName
):
647 elif options
.testStartsWith
is not None:
648 if not test
.name
.startswith(options
.testStartsWith
):
650 elif options
.combineStartsWith
is not None:
651 if not test
.combineName
.startswith(options
.combineStartsWith
):
654 if options
.maxTestCount
!= -1 and self
.counters
[self
.CNT_RAN
] >= options
.maxTestCount
:
656 self
.counters
[self
.CNT_RAN
] += 1
658 # Run the thing, dammit.
671 self
.reportTest(test
)
672 if options
.reportCombines
or options
.reportErrCombines
:
673 self
.addToCombines(test
)
674 self
.counters
= self
.updateCounters(test
, self
.counters
)
676 if options
.reportCombines
or options
.reportErrCombines
:
678 # Build a report for every single combine.
680 # TODO: How to sort a dict?
682 self
.combinesRan
.keys().sort(None)
683 for key
in self
.combinesRan
.keys():
684 self
.reportCombine(key
)
687 # Display the final report.
690 self
.displayShortResults(sys
.stdout
, True, None, self
.counters
)
692 sys
.stdout
.write("===========================\n")
693 self
.displayResults(sys
.stdout
, True, None, self
.counters
)