OASIS RelaxNG testsuite python script to run regression against OASIS

* test/relaxng/OASIS/spectest.xml: OASIS RelaxNG testsuite
* check-relaxng-test-suite.py: python script to run regression
  against OASIS RelaxNG testsuite
* relaxng.c: some cleanup tweaks
* HTMLparser.c globals.c: cleanups in comments
* doc/libxml2-api.xml: updated the API
* result/relaxng/*: errors moved files, so large diffs but
  no changes at the semantic level.
Daniel
diff --git a/check-relaxng-test-suite.py b/check-relaxng-test-suite.py
new file mode 100755
index 0000000..feb6817
--- /dev/null
+++ b/check-relaxng-test-suite.py
@@ -0,0 +1,326 @@
+#!/usr/bin/python
+import sys
+import time
+import os
+import string
+import StringIO
+sys.path.append("python")
+import libxml2
+
+
+#
+# the testsuite description
+#
+CONF="test/relaxng/OASIS/spectest.xml"
+LOG="check-relaxng-test-suite.log"
+
+log = open(LOG, "w")
+nb_schemas_tests = 0
+nb_schemas_success = 0
+nb_schemas_failed = 0
+nb_instances_tests = 0
+nb_instances_success = 0
+nb_instances_failed = 0
+
+libxml2.lineNumbersDefault(1)
+#
+# Error and warnng callbacks
+#
+def callback(ctx, str):
+    global log
+    log.write("%s%s" % (ctx, str))
+
+libxml2.registerErrorHandler(callback, "")
+
+#
+# Resolver callback
+#
+resources = {}
+def resolver(URL, ID, ctxt):
+    global resources
+
+    if resources.has_key(URL):
+        return(StringIO.StringIO(resources[URL]))
+    log.write("Resolver failure: asked %s\n" % (URL))
+    log.write("resources: %s\n" % (resources))
+    return None
+
+libxml2.setEntityLoader(resolver)
+
+#
+# handle a valid instance
+#
+def handle_valid(node, schema):
+    global log
+    global nb_instances_success
+    global nb_instances_failed
+
+    instance = ""
+    child = node.children
+    while child != None:
+        if child.type != 'text':
+	    instance = instance + child.serialize()
+	child = child.next
+
+    try:
+	doc = libxml2.parseDoc(instance)
+    except:
+        doc = None
+
+    if doc == None:
+        log.write("\nFailed to parse correct instance:\n-----\n")
+	log.write(instance)
+        log.write("\n-----\n")
+	nb_instances_failed = nb_instances_failed + 1
+	return
+
+    try:
+        ctxt = schema.relaxNGNewValidCtxt()
+	ret = doc.relaxNGValidateDoc(ctxt)
+    except:
+        ret = -1
+    if ret != 0:
+        log.write("\nFailed to validate correct instance:\n-----\n")
+	log.write(instance)
+        log.write("\n-----\n")
+	nb_instances_failed = nb_instances_failed + 1
+    else:
+	nb_instances_success = nb_instances_success + 1
+
+#
+# handle an invalid instance
+#
+def handle_invalid(node, schema):
+    global log
+    global nb_instances_success
+    global nb_instances_failed
+
+    instance = ""
+    child = node.children
+    while child != None:
+        if child.type != 'text':
+	    instance = instance + child.serialize()
+	child = child.next
+
+    try:
+	doc = libxml2.parseDoc(instance)
+    except:
+        doc = None
+
+    if doc == None:
+        log.write("\nStrange: failed to parse incorrect instance:\n-----\n")
+	log.write(instance)
+        log.write("\n-----\n")
+	return
+
+    try:
+        ctxt = schema.relaxNGNewValidCtxt()
+	ret = doc.relaxNGValidateDoc(ctxt)
+    except:
+        ret = -1
+    if ret == 0:
+        log.write("\nFailed to detect validation problem in instance:\n-----\n")
+	log.write(instance)
+        log.write("\n-----\n")
+	nb_instances_failed = nb_instances_failed + 1
+    else:
+	nb_instances_success = nb_instances_success + 1
+
+#
+# handle an incorrect test
+#
+def handle_correct(node):
+    global log
+    global nb_schemas_success
+    global nb_schemas_failed
+
+    schema = ""
+    child = node.children
+    while child != None:
+        if child.type != 'text':
+	    schema = schema + child.serialize()
+	child = child.next
+
+    try:
+	rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema))
+	rngs = rngp.relaxNGParse()
+    except:
+        rngs = None
+    if rngs == None:
+        log.write("\nFailed to compile correct schema:\n-----\n")
+	log.write(schema)
+        log.write("\n-----\n")
+	nb_schemas_failed = nb_schemas_failed + 1
+    else:
+	nb_schemas_success = nb_schemas_success + 1
+    return rngs
+        
+def handle_incorrect(node):
+    global log
+    global nb_schemas_success
+    global nb_schemas_failed
+
+    schema = ""
+    child = node.children
+    while child != None:
+        if child.type != 'text':
+	    schema = schema + child.serialize()
+	child = child.next
+
+    try:
+	rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema))
+	rngs = rngp.relaxNGParse()
+    except:
+        rngs = None
+    if rngs != None:
+        log.write("\nFailed to detect schema error in:\n-----\n")
+	log.write(schema)
+        log.write("\n-----\n")
+	nb_schemas_failed = nb_schemas_failed + 1
+    else:
+#	log.write("\nSuccess detecting schema error in:\n-----\n")
+#	log.write(schema)
+#	log.write("\n-----\n")
+	nb_schemas_success = nb_schemas_success + 1
+    return None
+
+#
+# resource handling: keep a dictionary of URL->string mappings
+#
+def handle_resource(node, dir):
+    global resources
+
+    try:
+	name = node.prop('name')
+    except:
+        name = None
+
+    if name == None or name == '':
+        log.write("resource has no name")
+	return;
+        
+    if dir != None:
+#        name = libxml2.buildURI(name, dir)
+        name = dir + '/' + name
+
+    res = ""
+    child = node.children
+    while child != None:
+        if child.type != 'text':
+	    res = res + child.serialize()
+	child = child.next
+    resources[name] = res
+
+#
+# dir handling: pseudo directory resources
+#
+def handle_dir(node, dir):
+    try:
+	name = node.prop('name')
+    except:
+        name = None
+
+    if name == None or name == '':
+        log.write("resource has no name")
+	return;
+        
+    if dir != None:
+#        name = libxml2.buildURI(name, dir)
+        name = dir + '/' + name
+
+    dirs = node.xpathEval('dir')
+    for dir in dirs:
+        handle_dir(dir, name)
+    res = node.xpathEval('resource')
+    for r in res:
+        handle_resource(r, name)
+
+
+#
+# handle a testCase element
+#
+def handle_testCase(node):
+    global nb_schemas_tests
+    global nb_instances_tests
+    global resources
+
+    log.write("\n    ============= test %d line %d ================\n" % (
+
+              nb_schemas_tests, node.lineNo()))
+    resources = {}
+
+    dirs = node.xpathEval('dir')
+    for dir in dirs:
+        handle_dir(dir, None)
+    res = node.xpathEval('resource')
+    for r in res:
+        handle_resource(r, None)
+
+    tsts = node.xpathEval('incorrect')
+    if tsts != []:
+        if len(tsts) != 1:
+	    print "warning test line %d has more than one <incorrect> example" %(node.lineNo())
+	schema = handle_incorrect(tsts[0])
+    else:
+        tsts = node.xpathEval('correct')
+	if tsts != []:
+	    if len(tsts) != 1:
+		print "warning test line %d has more than one <correct> example"% (node.lineNo())
+	    schema = handle_correct(tsts[0])
+	else:
+	    print "warning <testCase> line %d has no <correct> nor <incorrect> child" % (node.lineNo())
+
+    nb_schemas_tests = nb_schemas_tests + 1;
+    
+    valids = node.xpathEval('valid')
+    invalids = node.xpathEval('invalid')
+    nb_instances_tests = nb_instances_tests + len(valids) + len(invalids)
+    if schema != None:
+        for valid in valids:
+	    handle_valid(valid, schema)
+        for invalid in invalids:
+	    handle_invalid(invalid, schema)
+
+
+#
+# handle a testSuite element
+#
+def handle_testSuite(node):
+    docs = node.xpathEval('documentation')
+    authors = node.xpathEval('author')
+    if docs != []:
+        msg = ""
+        for doc in docs:
+	    msg = msg + doc.content + " "
+	if authors != []:
+	    msg = msg + "written by "
+	    for author in authors:
+	        msg = msg + author.content + " "
+	print msg
+    sections = node.xpathEval('section')
+    if sections != []:
+        msg = ""
+        for section in sections:
+	    msg = msg + section.content + " "
+        print "Tests for section %s" % (msg)
+    for test in node.xpathEval('testCase'):
+        handle_testCase(test)
+    for test in node.xpathEval('testSuite'):
+        handle_testSuite(test)
+	        
+
+#
+# Parse the conf file
+#
+testsuite = libxml2.parseFile(CONF)
+root = testsuite.getRootElement()
+if root.name != 'testSuite':
+    print "%s doesn't start with a testSuite element, aborting" % (CONF)
+    sys.exit(1)
+print "Running Relax NG testsuite"
+handle_testSuite(root)
+
+print "\nTOTAL:\nfound %d test schemas: %d success %d failures" % (
+      nb_schemas_tests, nb_schemas_success, nb_schemas_failed)
+print "found %d test instances: %d success %d failures" % (
+      nb_instances_tests, nb_instances_success, nb_instances_failed)