1#!/usr/bin/env python3 2import sys 3import time 4import os 5try: 6 # Python 2 7 from StringIO import StringIO 8except ImportError: 9 # Python 3 10 from io import StringIO 11sys.path.insert(0, "python") 12import libxml2 13 14# Memory debug specific 15libxml2.debugMemory(1) 16debug = 0 17verbose = 0 18quiet = 1 19 20# 21# the testsuite description 22# 23CONF=os.path.join(os.path.dirname(__file__), "test/relaxng/OASIS/spectest.xml") 24LOG="check-relaxng-test-suite.log" 25RES="relaxng-test-results.xml" 26 27log = open(LOG, "w") 28nb_schemas_tests = 0 29nb_schemas_success = 0 30nb_schemas_failed = 0 31nb_instances_tests = 0 32nb_instances_success = 0 33nb_instances_failed = 0 34 35libxml2.lineNumbersDefault(1) 36# 37# Error and warnng callbacks 38# 39def callback(ctx, str): 40 global log 41 log.write("%s%s" % (ctx, str)) 42 43libxml2.registerErrorHandler(callback, "") 44 45# 46# Resolver callback 47# 48resources = {} 49def resolver(URL, ID, ctxt): 50 global resources 51 52 if URL.find('#') != -1: 53 URL = URL[0:URL.find('#')] 54 if URL in resources: 55 return(StringIO(resources[URL])) 56 log.write("Resolver failure: asked %s\n" % (URL)) 57 log.write("resources: %s\n" % (resources)) 58 return None 59 60# 61# Load the previous results 62# 63#results = {} 64#previous = {} 65# 66#try: 67# res = libxml2.parseFile(RES) 68#except: 69# log.write("Could not parse %s" % (RES)) 70 71# 72# handle a valid instance 73# 74def handle_valid(node, schema): 75 global log 76 global nb_instances_success 77 global nb_instances_failed 78 79 instance = "" 80 child = node.children 81 while child != None: 82 if child.type != 'text': 83 instance = instance + child.serialize() 84 child = child.next 85 86 try: 87 doc = libxml2.parseDoc(instance) 88 except: 89 doc = None 90 91 if doc is None: 92 log.write("\nFailed to parse correct instance:\n-----\n") 93 log.write(instance) 94 log.write("\n-----\n") 95 nb_instances_failed = nb_instances_failed + 1 96 return 97 98 try: 99 ctxt = schema.relaxNGNewValidCtxt() 100 ret = doc.relaxNGValidateDoc(ctxt) 101 except: 102 ret = -1 103 if ret != 0: 104 log.write("\nFailed to validate correct instance:\n-----\n") 105 log.write(instance) 106 log.write("\n-----\n") 107 nb_instances_failed = nb_instances_failed + 1 108 else: 109 nb_instances_success = nb_instances_success + 1 110 doc.freeDoc() 111 112# 113# handle an invalid instance 114# 115def handle_invalid(node, schema): 116 global log 117 global nb_instances_success 118 global nb_instances_failed 119 120 instance = "" 121 child = node.children 122 while child != None: 123 if child.type != 'text': 124 instance = instance + child.serialize() 125 child = child.next 126 127 try: 128 doc = libxml2.parseDoc(instance) 129 except: 130 doc = None 131 132 if doc is None: 133 log.write("\nStrange: failed to parse incorrect instance:\n-----\n") 134 log.write(instance) 135 log.write("\n-----\n") 136 return 137 138 try: 139 ctxt = schema.relaxNGNewValidCtxt() 140 ret = doc.relaxNGValidateDoc(ctxt) 141 except: 142 ret = -1 143 if ret == 0: 144 log.write("\nFailed to detect validation problem in instance:\n-----\n") 145 log.write(instance) 146 log.write("\n-----\n") 147 nb_instances_failed = nb_instances_failed + 1 148 else: 149 nb_instances_success = nb_instances_success + 1 150 doc.freeDoc() 151 152# 153# handle an incorrect test 154# 155def handle_correct(node): 156 global log 157 global nb_schemas_success 158 global nb_schemas_failed 159 160 schema = "" 161 child = node.children 162 while child != None: 163 if child.type != 'text': 164 schema = schema + child.serialize() 165 child = child.next 166 167 try: 168 rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema)) 169 rngs = rngp.relaxNGParse() 170 except: 171 rngs = None 172 if rngs is None: 173 log.write("\nFailed to compile correct schema:\n-----\n") 174 log.write(schema) 175 log.write("\n-----\n") 176 nb_schemas_failed = nb_schemas_failed + 1 177 else: 178 nb_schemas_success = nb_schemas_success + 1 179 return rngs 180 181def handle_incorrect(node): 182 global log 183 global nb_schemas_success 184 global nb_schemas_failed 185 186 schema = "" 187 child = node.children 188 while child != None: 189 if child.type != 'text': 190 schema = schema + child.serialize() 191 child = child.next 192 193 try: 194 rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema)) 195 rngs = rngp.relaxNGParse() 196 except: 197 rngs = None 198 if rngs != None: 199 log.write("\nFailed to detect schema error in:\n-----\n") 200 log.write(schema) 201 log.write("\n-----\n") 202 nb_schemas_failed = nb_schemas_failed + 1 203 else: 204# log.write("\nSuccess detecting schema error in:\n-----\n") 205# log.write(schema) 206# log.write("\n-----\n") 207 nb_schemas_success = nb_schemas_success + 1 208 return None 209 210# 211# resource handling: keep a dictionary of URL->string mappings 212# 213def handle_resource(node, dir): 214 global resources 215 216 try: 217 name = node.prop('name') 218 except: 219 name = None 220 221 if name is None or name == '': 222 log.write("resource has no name") 223 return; 224 225 if dir != None: 226# name = libxml2.buildURI(name, dir) 227 name = dir + '/' + name 228 229 res = "" 230 child = node.children 231 while child != None: 232 if child.type != 'text': 233 res = res + child.serialize() 234 child = child.next 235 resources[name] = res 236 237# 238# dir handling: pseudo directory resources 239# 240def handle_dir(node, dir): 241 try: 242 name = node.prop('name') 243 except: 244 name = None 245 246 if name is None or name == '': 247 log.write("resource has no name") 248 return; 249 250 if dir != None: 251# name = libxml2.buildURI(name, dir) 252 name = dir + '/' + name 253 254 dirs = node.xpathEval('dir') 255 for dir in dirs: 256 handle_dir(dir, name) 257 res = node.xpathEval('resource') 258 for r in res: 259 handle_resource(r, name) 260 261# 262# handle a testCase element 263# 264def handle_testCase(node): 265 global nb_schemas_tests 266 global nb_instances_tests 267 global resources 268 269 sections = node.xpathEval('string(section)') 270 log.write("\n ======== test %d line %d section %s ==========\n" % ( 271 272 nb_schemas_tests, node.lineNo(), sections)) 273 resources = {} 274 if debug: 275 print("test %d line %d" % (nb_schemas_tests, node.lineNo())) 276 277 dirs = node.xpathEval('dir') 278 for dir in dirs: 279 handle_dir(dir, None) 280 res = node.xpathEval('resource') 281 for r in res: 282 handle_resource(r, None) 283 284 tsts = node.xpathEval('incorrect') 285 if tsts != []: 286 if len(tsts) != 1: 287 print("warning test line %d has more than one <incorrect> example" %(node.lineNo())) 288 schema = handle_incorrect(tsts[0]) 289 else: 290 tsts = node.xpathEval('correct') 291 if tsts != []: 292 if len(tsts) != 1: 293 print("warning test line %d has more than one <correct> example"% (node.lineNo())) 294 schema = handle_correct(tsts[0]) 295 else: 296 print("warning <testCase> line %d has no <correct> nor <incorrect> child" % (node.lineNo())) 297 298 nb_schemas_tests = nb_schemas_tests + 1; 299 300 valids = node.xpathEval('valid') 301 invalids = node.xpathEval('invalid') 302 nb_instances_tests = nb_instances_tests + len(valids) + len(invalids) 303 if schema != None: 304 for valid in valids: 305 handle_valid(valid, schema) 306 for invalid in invalids: 307 handle_invalid(invalid, schema) 308 309 310# 311# handle a testSuite element 312# 313def handle_testSuite(node, level = 0): 314 global nb_schemas_tests, nb_schemas_success, nb_schemas_failed 315 global nb_instances_tests, nb_instances_success, nb_instances_failed 316 global quiet 317 if level >= 1: 318 old_schemas_tests = nb_schemas_tests 319 old_schemas_success = nb_schemas_success 320 old_schemas_failed = nb_schemas_failed 321 old_instances_tests = nb_instances_tests 322 old_instances_success = nb_instances_success 323 old_instances_failed = nb_instances_failed 324 325 docs = node.xpathEval('documentation') 326 authors = node.xpathEval('author') 327 if docs != []: 328 msg = "" 329 for doc in docs: 330 msg = msg + doc.content + " " 331 if authors != []: 332 msg = msg + "written by " 333 for author in authors: 334 msg = msg + author.content + " " 335 if quiet == 0: 336 print(msg) 337 sections = node.xpathEval('section') 338 if sections != [] and level <= 0: 339 msg = "" 340 for section in sections: 341 msg = msg + section.content + " " 342 if quiet == 0: 343 print("Tests for section %s" % (msg)) 344 for test in node.xpathEval('testCase'): 345 handle_testCase(test) 346 for test in node.xpathEval('testSuite'): 347 handle_testSuite(test, level + 1) 348 349 350 if verbose and level >= 1 and sections != []: 351 msg = "" 352 for section in sections: 353 msg = msg + section.content + " " 354 print("Result of tests for section %s" % (msg)) 355 if nb_schemas_tests != old_schemas_tests: 356 print("found %d test schemas: %d success %d failures" % ( 357 nb_schemas_tests - old_schemas_tests, 358 nb_schemas_success - old_schemas_success, 359 nb_schemas_failed - old_schemas_failed)) 360 if nb_instances_tests != old_instances_tests: 361 print("found %d test instances: %d success %d failures" % ( 362 nb_instances_tests - old_instances_tests, 363 nb_instances_success - old_instances_success, 364 nb_instances_failed - old_instances_failed)) 365# 366# Parse the conf file 367# 368libxml2.substituteEntitiesDefault(1); 369testsuite = libxml2.parseFile(CONF) 370libxml2.setEntityLoader(resolver) 371root = testsuite.getRootElement() 372if root.name != 'testSuite': 373 print("%s doesn't start with a testSuite element, aborting" % (CONF)) 374 sys.exit(1) 375if quiet == 0: 376 print("Running Relax NG testsuite") 377handle_testSuite(root) 378 379if quiet == 0: 380 print("\nTOTAL:\n") 381if quiet == 0 or nb_schemas_failed != 0: 382 print("found %d test schemas: %d success %d failures" % ( 383 nb_schemas_tests, nb_schemas_success, nb_schemas_failed)) 384if quiet == 0 or nb_instances_failed != 0: 385 print("found %d test instances: %d success %d failures" % ( 386 nb_instances_tests, nb_instances_success, nb_instances_failed)) 387 388testsuite.freeDoc() 389 390# Memory debug specific 391libxml2.relaxNGCleanupTypes() 392libxml2.cleanupParser() 393if libxml2.debugMemory(1) == 0: 394 if quiet == 0: 395 print("OK") 396else: 397 print("Memory leak %d bytes" % (libxml2.debugMemory(1))) 398 libxml2.dumpMemory() 399