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