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