Annotation of embedaddon/libxml2/xstc/xstc.py, revision 1.1.1.1
1.1 misho 1: #!/usr/bin/env python
2:
3: #
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.
6: #
7:
8: import sys, os
9: import exceptions, optparse
10: import libxml2
11:
12: opa = optparse.OptionParser()
13:
14: opa.add_option("-b", "--base", action="store", type="string", dest="baseDir",
15: default="",
16: help="""The base directory; i.e. the parent folder of the
17: "nisttest", "suntest" and "msxsdtest" directories.""")
18:
19: opa.add_option("-o", "--out", action="store", type="string", dest="logFile",
20: default="test.log",
21: help="The filepath of the log file to be created")
22:
23: opa.add_option("--log", action="store_true", dest="enableLog",
24: default=False,
25: help="Create the log file")
26:
27: opa.add_option("--no-test-out", action="store_true", dest="disableTestStdOut",
28: default=False,
29: help="Don't output test results")
30:
31: opa.add_option("-s", "--silent", action="store_true", dest="silent", default=False,
32: help="Disables display of all tests")
33:
34: opa.add_option("-v", "--verbose", action="store_true", dest="verbose",
35: default=False,
36: help="Displays all tests (only if --silent is not set)")
37:
38: opa.add_option("-x", "--max", type="int", dest="maxTestCount",
39: default="-1",
40: help="The maximum number of tests to be run")
41:
42: opa.add_option("-t", "--test", type="string", dest="singleTest",
43: default=None,
44: help="Runs the specified test only")
45:
46: opa.add_option("--tsw", "--test-starts-with", type="string", dest="testStartsWith",
47: default=None,
48: help="Runs the specified test(s), starting with the given string")
49:
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")
53:
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")
57:
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")
61:
62: opa.add_option("-c", "--combines", type="string", dest="combines",
63: default=None,
64: help="Combines to be run (all if omitted)")
65:
66: opa.add_option("--csw", "--csw", type="string", dest="combineStartsWith",
67: default=None,
68: help="Combines to be run (all if omitted)")
69:
70: opa.add_option("--rc", "--report-combines", action="store_true",
71: dest="reportCombines", default=False,
72: help="Display combine reports")
73:
74: opa.add_option("--rec", "--report-err-combines", action="store_true",
75: dest="reportErrCombines", default=False,
76: help="Display erroneous combine reports only")
77:
78: opa.add_option("--debug", action="store_true",
79: dest="debugEnabled", default=False,
80: help="Displays debug messages")
81:
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.")
91:
92: (options, args) = opa.parse_args()
93:
94: if options.combines is not None:
95: options.combines = options.combines.split()
96:
97: ################################################
98: # The vars below are not intended to be changed.
99: #
100:
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."
105: vendorNIST = "NIST"
106: vendorNIST_2 = "NIST-2"
107: vendorSUN = "SUN"
108: vendorMS = "MS"
109:
110: ###################
111: # Helper functions.
112: #
113: vendor = None
114:
115: def handleError(test, msg):
116: global options
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:
122: test.failInternal()
123:
124:
125: def fixFileNames(fileName):
126: if (fileName is None) or (fileName == ""):
127: return ""
128: dirs = fileName.split("/")
129: if dirs[1] != "Tests":
130: fileName = os.path.join(".", "Tests")
131: for dir in dirs[1:]:
132: fileName = os.path.join(fileName, dir)
133: return fileName
134:
135: class XSTCTestGroup:
136: def __init__(self, name, schemaFileName, descr):
137: global vendor, vendorNIST_2
138: self.name = name
139: self.descr = descr
140: self.mainSchema = True
141: self.schemaFileName = fixFileNames(schemaFileName)
142: self.schemaParsed = False
143: self.schemaTried = False
144:
145: def setSchema(self, schemaFileName, parsed):
146: if not self.mainSchema:
147: return
148: self.mainSchema = False
149: self.schemaParsed = parsed
150: self.schemaTried = True
151:
152: class XSTCTestCase:
153:
154: # <!-- groupName, Name, Accepted, File, Val, Descr
155: def __init__(self, isSchema, groupName, name, accepted, file, val, descr):
156: global options
157: #
158: # Constructor.
159: #
160: self.testRunner = None
161: self.isSchema = isSchema
162: self.groupName = groupName
163: self.name = name
164: self.accepted = accepted
165: self.fileName = fixFileNames(file)
166: self.val = val
167: self.descr = descr
168: self.failed = False
169: self.combineName = None
170:
171: self.log = []
172: self.libLog = []
173: self.initialMemUsed = 0
174: self.memLeak = 0
175: self.excepted = False
176: self.bad = False
177: self.unimplemented = False
178: self.internalErr = False
179: self.noSchemaErr = False
180: self.failed = False
181: #
182: # Init the log.
183: #
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))
188:
189: def initTest(self, runner):
190: global vendorNIST, vendorSUN, vendorMS, vendorNIST_2, options, vendor
191: #
192: # Get the test-group.
193: #
194: self.runner = runner
195: self.group = runner.getGroup(self.groupName)
196: if vendor == vendorMS or vendor == vendorSUN:
197: #
198: # Use the last given directory for the combine name.
199: #
200: dirs = self.fileName.split("/")
201: self.combineName = dirs[len(dirs) -2]
202: elif vendor == vendorNIST:
203: #
204: # NIST files are named in the following form:
205: # "NISTSchema-short-pattern-1.xsd"
206: #
207: tokens = self.name.split("-")
208: self.combineName = tokens[1]
209: elif vendor == vendorNIST_2:
210: #
211: # Group-names have the form: "atomic-normalizedString-length-1"
212: #
213: tokens = self.groupName.split("-")
214: self.combineName = "%s-%s" % (tokens[0], tokens[1])
215: else:
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))
220:
221:
222: def addLibLog(self, msg):
223: """This one is intended to be used by the error handler
224: function"""
225: global options
226: if not options.silent:
227: self.libLog.append(msg)
228:
229: def fail(self, msg):
230: global options
231: self.failed = True
232: if not options.silent:
233: self.log.append("'%s' ( FAILED: %s\n" % (self.name, msg))
234:
235: def failNoSchema(self):
236: global options
237: self.failed = True
238: self.noSchemaErr = True
239: if not options.silent:
240: self.log.append("'%s' X NO-SCHEMA\n" % (self.name))
241:
242: def failInternal(self):
243: global options
244: self.failed = True
245: self.internalErr = True
246: if not options.silent:
247: self.log.append("'%s' * INTERNAL\n" % self.name)
248:
249: def failUnimplemented(self):
250: global options
251: self.failed = True
252: self.unimplemented = True
253: if not options.silent:
254: self.log.append("'%s' ? UNIMPLEMENTED\n" % self.name)
255:
256: def failCritical(self, msg):
257: global options
258: self.failed = True
259: self.bad = True
260: if not options.silent:
261: self.log.append("'%s' ! BAD: %s\n" % (self.name, msg))
262:
263: def failExcept(self, e):
264: global options
265: self.failed = True
266: self.excepted = True
267: if not options.silent:
268: self.log.append("'%s' # EXCEPTION: %s\n" % (self.name, e.__str__()))
269:
270: def setUp(self):
271: #
272: # Set up Libxml2.
273: #
274: self.initialMemUsed = libxml2.debugMemory(1)
275: libxml2.initParser()
276: libxml2.lineNumbersDefault(1)
277: libxml2.registerErrorHandler(handleError, self)
278:
279: def tearDown(self):
280: libxml2.schemaCleanupTypes()
281: libxml2.cleanupParser()
282: self.memLeak = libxml2.debugMemory(1) - self.initialMemUsed
283:
284: def isIOError(self, file, docType):
285: err = None
286: try:
287: err = libxml2.lastError()
288: except:
289: # Suppress exceptions.
290: pass
291: if (err is None):
292: return False
293: if err.domain() == libxml2.XML_FROM_IO:
294: self.failCritical("failed to access the %s resource '%s'\n" % (docType, file))
295:
296: def debugMsg(self, msg):
297: global options
298: if options.debugEnabled:
299: sys.stdout.write("'%s' DEBUG: %s\n" % (self.name, msg))
300:
301: def finalize(self):
302: global options
303: """Adds additional info to the log."""
304: #
305: # Add libxml2 messages.
306: #
307: if not options.silent:
308: self.log.extend(self.libLog)
309: #
310: # Add memory leaks.
311: #
312: if self.memLeak != 0:
313: self.log.append("%s + memory leak: %d bytes\n" % (self.name, self.memLeak))
314:
315: def run(self):
316: """Runs a test."""
317: global options
318:
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)
323: try:
324: self.validate()
325: except (Exception, libxml2.parserError, libxml2.treeError), e:
326: self.failExcept(e)
327:
328: def parseSchema(fileName):
329: schema = None
330: ctxt = libxml2.schemaNewParserCtxt(fileName)
331: try:
332: try:
333: schema = ctxt.schemaParse()
334: except:
335: pass
336: finally:
337: del ctxt
338: return schema
339:
340:
341: class XSTCSchemaTest(XSTCTestCase):
342:
343: def __init__(self, groupName, name, accepted, file, val, descr):
344: XSTCTestCase.__init__(self, 1, groupName, name, accepted, file, val, descr)
345:
346: def validate(self):
347: global msgSchemaNotValidButShould, msgSchemaValidButShouldNot
348: schema = None
349: filePath = self.fileName
350: # os.path.join(options.baseDir, self.fileName)
351: valid = 0
352: try:
353: #
354: # Parse the schema.
355: #
356: self.debugMsg("loading schema: %s" % filePath)
357: schema = parseSchema(filePath)
358: self.debugMsg("after loading schema")
359: if schema is None:
360: self.debugMsg("schema is None")
361: self.debugMsg("checking for IO errors...")
362: if self.isIOError(file, "schema"):
363: return
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")
367: if (schema == None):
368: self.fail(msgSchemaNotValidButShould)
369: else:
370: self.fail(msgSchemaValidButShouldNot)
371: else:
372: self.debugMsg("schema result is OK")
373: finally:
374: self.group.setSchema(self.fileName, schema is not None)
375: del schema
376:
377: class XSTCInstanceTest(XSTCTestCase):
378:
379: def __init__(self, groupName, name, accepted, file, val, descr):
380: XSTCTestCase.__init__(self, 0, groupName, name, accepted, file, val, descr)
381:
382: def validate(self):
383: instance = None
384: schema = None
385: filePath = self.fileName
386: # os.path.join(options.baseDir, self.fileName)
387:
388: if not self.group.schemaParsed and self.group.schemaTried:
389: self.failNoSchema()
390: return
391:
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
396: # be already raised?
397: raise Exception("Could not create the instance parser context.")
398: if not options.validationSAX:
399: try:
400: try:
401: instance = parserCtxt.ctxtReadFile(filePath, None, libxml2.XML_PARSE_NOWARNING)
402: except:
403: # Suppress exceptions.
404: pass
405: finally:
406: del parserCtxt
407: self.debugMsg("after loading instance")
408: if instance is None:
409: self.debugMsg("instance is None")
410: self.failCritical("Failed to parse the instance for unknown reasons.")
411: return
412: try:
413: #
414: # Validate the instance.
415: #
416: self.debugMsg("loading schema: %s" % self.group.schemaFileName)
417: schema = parseSchema(self.group.schemaFileName)
418: try:
419: validationCtxt = schema.schemaNewValidCtxt()
420: #validationCtxt = libxml2.schemaNewValidCtxt(None)
421: if (validationCtxt is None):
422: self.failCritical("Could not create the validation context.")
423: return
424: try:
425: self.debugMsg("validating instance")
426: if options.validationSAX:
427: instance_Err = validationCtxt.schemaValidateFile(filePath, 0)
428: else:
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)
436: else:
437: self.fail(msgInstanceValidButShouldNot)
438:
439: else:
440: self.debugMsg("instance result is OK")
441: finally:
442: del validationCtxt
443: finally:
444: del schema
445: finally:
446: if instance is not None:
447: instance.freeDoc()
448:
449:
450: ####################
451: # Test runner class.
452: #
453:
454: class XSTCTestRunner:
455:
456: CNT_TOTAL = 0
457: CNT_RAN = 1
458: CNT_SUCCEEDED = 2
459: CNT_FAILED = 3
460: CNT_UNIMPLEMENTED = 4
461: CNT_INTERNAL = 5
462: CNT_BAD = 6
463: CNT_EXCEPTED = 7
464: CNT_MEMLEAK = 8
465: CNT_NOSCHEMA = 9
466: CNT_NOTACCEPTED = 10
467: CNT_SCHEMA_TEST = 11
468:
469: def __init__(self):
470: self.logFile = None
471: self.counters = self.createCounters()
472: self.testList = []
473: self.combinesRan = {}
474: self.groups = {}
475: self.curGroup = None
476:
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}
482:
483: return counters
484:
485: def addTest(self, test):
486: self.testList.append(test)
487: test.initTest(self)
488:
489: def getGroup(self, groupName):
490: return self.groups[groupName]
491:
492: def addGroup(self, group):
493: self.groups[group.name] = group
494:
495: def updateCounters(self, test, counters):
496: if test.memLeak != 0:
497: counters[self.CNT_MEMLEAK] += 1
498: if not test.failed:
499: counters[self.CNT_SUCCEEDED] +=1
500: if test.failed:
501: counters[self.CNT_FAILED] += 1
502: if test.bad:
503: counters[self.CNT_BAD] += 1
504: if test.unimplemented:
505: counters[self.CNT_UNIMPLEMENTED] += 1
506: if test.internalErr:
507: counters[self.CNT_INTERNAL] += 1
508: if test.noSchemaErr:
509: counters[self.CNT_NOSCHEMA] += 1
510: if test.excepted:
511: counters[self.CNT_EXCEPTED] += 1
512: if not test.accepted:
513: counters[self.CNT_NOTACCEPTED] += 1
514: if test.isSchema:
515: counters[self.CNT_SCHEMA_TEST] += 1
516: return counters
517:
518: def displayResults(self, out, all, combName, counters):
519: out.write("\n")
520: if all:
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])
539:
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]))
549: out.write(" (")
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]))
560: out.write(" )")
561: if counters[self.CNT_MEMLEAK] > 0:
562: out.write(" %d leaks" % (counters[self.CNT_MEMLEAK]))
563: out.write("\n")
564: else:
565: out.write(" all passed\n")
566:
567: def reportCombine(self, combName):
568: global options
569:
570: counters = self.createCounters()
571: #
572: # Compute evaluation counters.
573: #
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):
579: pass
580: else:
581: if options.enableLog:
582: self.displayResults(self.logFile, False, combName, counters)
583: self.displayResults(sys.stdout, False, combName, counters)
584:
585: def displayTestLog(self, test):
586: sys.stdout.writelines(test.log)
587: sys.stdout.write("~~~~~~~~~~\n")
588:
589: def reportTest(self, test):
590: global options
591:
592: error = test.failed or test.memLeak != 0
593: #
594: # Only erroneous tests will be written to the log,
595: # except @verbose is switched on.
596: #
597: if options.enableLog and (options.verbose or error):
598: self.logFile.writelines(test.log)
599: self.logFile.write("~~~~~~~~~~\n")
600: #
601: # if not @silent, only erroneous tests will be
602: # written to stdout, except @verbose is switched on.
603: #
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)
613:
614:
615: def addToCombines(self, test):
616: found = False
617: if self.combinesRan.has_key(test.combineName):
618: self.combinesRan[test.combineName].append(test)
619: else:
620: self.combinesRan[test.combineName] = [test]
621:
622: def run(self):
623:
624: global options
625:
626: if options.info:
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())
631: return
632:
633: if options.enableLog:
634: self.logFile = open(options.logFile, "w")
635: try:
636: for test in self.testList:
637: self.counters[self.CNT_TOTAL] += 1
638: #
639: # Filter tests.
640: #
641: if options.singleTest is not None and options.singleTest != "":
642: if (test.name != options.singleTest):
643: continue
644: elif options.combines is not None:
645: if not options.combines.__contains__(test.combineName):
646: continue
647: elif options.testStartsWith is not None:
648: if not test.name.startswith(options.testStartsWith):
649: continue
650: elif options.combineStartsWith is not None:
651: if not test.combineName.startswith(options.combineStartsWith):
652: continue
653:
654: if options.maxTestCount != -1 and self.counters[self.CNT_RAN] >= options.maxTestCount:
655: break
656: self.counters[self.CNT_RAN] += 1
657: #
658: # Run the thing, dammit.
659: #
660: try:
661: test.setUp()
662: try:
663: test.run()
664: finally:
665: test.tearDown()
666: finally:
667: #
668: # Evaluate.
669: #
670: test.finalize()
671: self.reportTest(test)
672: if options.reportCombines or options.reportErrCombines:
673: self.addToCombines(test)
674: self.counters = self.updateCounters(test, self.counters)
675: finally:
676: if options.reportCombines or options.reportErrCombines:
677: #
678: # Build a report for every single combine.
679: #
680: # TODO: How to sort a dict?
681: #
682: self.combinesRan.keys().sort(None)
683: for key in self.combinesRan.keys():
684: self.reportCombine(key)
685:
686: #
687: # Display the final report.
688: #
689: if options.silent:
690: self.displayShortResults(sys.stdout, True, None, self.counters)
691: else:
692: sys.stdout.write("===========================\n")
693: self.displayResults(sys.stdout, True, None, self.counters)
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>