• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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