• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python -u
2#
3# generate a tester program for the API
4#
5import sys
6import os
7import string
8try:
9    import libxml2
10except:
11    print "libxml2 python bindings not available, skipping testapi.c generation"
12    sys.exit(0)
13
14if len(sys.argv) > 1:
15    srcPref = sys.argv[1] + '/'
16else:
17    srcPref = ''
18
19#
20# Modules we want to skip in API test
21#
22skipped_modules = [ "SAX", "xlink", "threads", "globals",
23  "xmlmemory", "xmlversion", "xmlexports",
24  #deprecated
25  "DOCBparser",
26]
27
28#
29# defines for each module
30#
31modules_defines = {
32    "HTMLparser": "LIBXML_HTML_ENABLED",
33    "catalog": "LIBXML_CATALOG_ENABLED",
34    "xmlreader": "LIBXML_READER_ENABLED",
35    "relaxng": "LIBXML_SCHEMAS_ENABLED",
36    "schemasInternals": "LIBXML_SCHEMAS_ENABLED",
37    "xmlschemas": "LIBXML_SCHEMAS_ENABLED",
38    "xmlschemastypes": "LIBXML_SCHEMAS_ENABLED",
39    "xpath": "LIBXML_XPATH_ENABLED",
40    "xpathInternals": "LIBXML_XPATH_ENABLED",
41    "xinclude": "LIBXML_XINCLUDE_ENABLED",
42    "xpointer": "LIBXML_XPTR_ENABLED",
43    "xmlregexp" : "LIBXML_REGEXP_ENABLED",
44    "xmlautomata" : "LIBXML_AUTOMATA_ENABLED",
45    "xmlsave" : "LIBXML_OUTPUT_ENABLED",
46    "DOCBparser" : "LIBXML_DOCB_ENABLED",
47    "xmlmodule" : "LIBXML_MODULES_ENABLED",
48    "pattern" : "LIBXML_PATTERN_ENABLED",
49    "schematron" : "LIBXML_SCHEMATRON_ENABLED",
50}
51
52#
53# defines for specific functions
54#
55function_defines = {
56    "htmlDefaultSAXHandlerInit": "LIBXML_HTML_ENABLED",
57    "xmlSAX2EndElement" : "LIBXML_SAX1_ENABLED",
58    "xmlSAX2StartElement" : "LIBXML_SAX1_ENABLED",
59    "xmlSAXDefaultVersion" : "LIBXML_SAX1_ENABLED",
60    "UTF8Toisolat1" : "LIBXML_OUTPUT_ENABLED",
61    "xmlCleanupPredefinedEntities": "LIBXML_LEGACY_ENABLED",
62    "xmlInitializePredefinedEntities": "LIBXML_LEGACY_ENABLED",
63    "xmlSetFeature": "LIBXML_LEGACY_ENABLED",
64    "xmlGetFeature": "LIBXML_LEGACY_ENABLED",
65    "xmlGetFeaturesList": "LIBXML_LEGACY_ENABLED",
66    "xmlIOParseDTD": "LIBXML_VALID_ENABLED",
67    "xmlParseDTD": "LIBXML_VALID_ENABLED",
68    "xmlParseDoc": "LIBXML_SAX1_ENABLED",
69    "xmlParseMemory": "LIBXML_SAX1_ENABLED",
70    "xmlRecoverDoc": "LIBXML_SAX1_ENABLED",
71    "xmlParseFile": "LIBXML_SAX1_ENABLED",
72    "xmlRecoverFile": "LIBXML_SAX1_ENABLED",
73    "xmlRecoverMemory": "LIBXML_SAX1_ENABLED",
74    "xmlSAXParseFileWithData": "LIBXML_SAX1_ENABLED",
75    "xmlSAXParseMemory": "LIBXML_SAX1_ENABLED",
76    "xmlSAXUserParseMemory": "LIBXML_SAX1_ENABLED",
77    "xmlSAXParseDoc": "LIBXML_SAX1_ENABLED",
78    "xmlSAXParseDTD": "LIBXML_SAX1_ENABLED",
79    "xmlSAXUserParseFile": "LIBXML_SAX1_ENABLED",
80    "xmlParseEntity": "LIBXML_SAX1_ENABLED",
81    "xmlParseExternalEntity": "LIBXML_SAX1_ENABLED",
82    "xmlSAXParseMemoryWithData": "LIBXML_SAX1_ENABLED",
83    "xmlParseBalancedChunkMemory": "LIBXML_SAX1_ENABLED",
84    "xmlParseBalancedChunkMemoryRecover": "LIBXML_SAX1_ENABLED",
85    "xmlSetupParserForBuffer": "LIBXML_SAX1_ENABLED",
86    "xmlStopParser": "LIBXML_PUSH_ENABLED",
87    "xmlAttrSerializeTxtContent": "LIBXML_OUTPUT_ENABLED",
88    "xmlSAXParseFile": "LIBXML_SAX1_ENABLED",
89    "xmlSAXParseEntity": "LIBXML_SAX1_ENABLED",
90    "xmlNewTextChild": "LIBXML_TREE_ENABLED",
91    "xmlNewDocRawNode": "LIBXML_TREE_ENABLED",
92    "xmlNewProp": "LIBXML_TREE_ENABLED",
93    "xmlReconciliateNs": "LIBXML_TREE_ENABLED",
94    "xmlValidateNCName": "LIBXML_TREE_ENABLED",
95    "xmlValidateNMToken": "LIBXML_TREE_ENABLED",
96    "xmlValidateName": "LIBXML_TREE_ENABLED",
97    "xmlNewChild": "LIBXML_TREE_ENABLED",
98    "xmlValidateQName": "LIBXML_TREE_ENABLED",
99    "xmlSprintfElementContent": "LIBXML_OUTPUT_ENABLED",
100    "xmlValidGetPotentialChildren" : "LIBXML_VALID_ENABLED",
101    "xmlValidGetValidElements" : "LIBXML_VALID_ENABLED",
102    "docbDefaultSAXHandlerInit" : "LIBXML_DOCB_ENABLED",
103    "xmlTextReaderPreservePattern" : "LIBXML_PATTERN_ENABLED",
104}
105
106#
107# Some functions really need to be skipped for the tests.
108#
109skipped_functions = [
110# block on I/O
111"xmlFdRead", "xmlReadFd", "xmlCtxtReadFd",
112"htmlFdRead", "htmlReadFd", "htmlCtxtReadFd",
113"xmlReaderNewFd", "xmlReaderForFd",
114"xmlIORead", "xmlReadIO", "xmlCtxtReadIO",
115"htmlIORead", "htmlReadIO", "htmlCtxtReadIO",
116"xmlReaderNewIO", "xmlBufferDump", "xmlNanoFTPConnect",
117"xmlNanoFTPConnectTo", "xmlNanoHTTPMethod", "xmlNanoHTTPMethodRedir",
118# Complex I/O APIs
119"xmlCreateIOParserCtxt", "xmlParserInputBufferCreateIO",
120"xmlRegisterInputCallbacks", "xmlReaderForIO",
121"xmlOutputBufferCreateIO", "xmlRegisterOutputCallbacks",
122"xmlSaveToIO", "xmlIOHTTPOpenW",
123# library state cleanup, generate false leak information and other
124# troubles, heavillyb tested otherwise.
125"xmlCleanupParser", "xmlRelaxNGCleanupTypes", "xmlSetListDoc",
126"xmlSetTreeDoc", "xmlUnlinkNode",
127# hard to avoid leaks in the tests
128"xmlStrcat", "xmlStrncat", "xmlCatalogAddLocal", "xmlNewTextWriterDoc",
129"xmlXPathNewValueTree", "xmlXPathWrapString",
130# unimplemented
131"xmlTextReaderReadInnerXml", "xmlTextReaderReadOuterXml",
132"xmlTextReaderReadString",
133# destructor
134"xmlListDelete", "xmlOutputBufferClose", "xmlNanoFTPClose", "xmlNanoHTTPClose",
135# deprecated
136"xmlCatalogGetPublic", "xmlCatalogGetSystem", "xmlEncodeEntities",
137"xmlNewGlobalNs", "xmlHandleEntity", "xmlNamespaceParseNCName",
138"xmlNamespaceParseNSDef", "xmlNamespaceParseQName",
139"xmlParseNamespace", "xmlParseQuotedString", "xmlParserHandleReference",
140"xmlScanName",
141"xmlDecodeEntities",
142# allocators
143"xmlMemFree",
144# verbosity
145"xmlCatalogSetDebug", "xmlShellPrintXPathError", "xmlShellPrintNode",
146# Internal functions, no user space should really call them
147"xmlParseAttribute", "xmlParseAttributeListDecl", "xmlParseName",
148"xmlParseNmtoken", "xmlParseEntityValue", "xmlParseAttValue",
149"xmlParseSystemLiteral", "xmlParsePubidLiteral", "xmlParseCharData",
150"xmlParseExternalID", "xmlParseComment", "xmlParsePITarget", "xmlParsePI",
151"xmlParseNotationDecl", "xmlParseEntityDecl", "xmlParseDefaultDecl",
152"xmlParseNotationType", "xmlParseEnumerationType", "xmlParseEnumeratedType",
153"xmlParseAttributeType", "xmlParseAttributeListDecl",
154"xmlParseElementMixedContentDecl", "xmlParseElementChildrenContentDecl",
155"xmlParseElementContentDecl", "xmlParseElementDecl", "xmlParseMarkupDecl",
156"xmlParseCharRef", "xmlParseEntityRef", "xmlParseReference",
157"xmlParsePEReference", "xmlParseDocTypeDecl", "xmlParseAttribute",
158"xmlParseStartTag", "xmlParseEndTag", "xmlParseCDSect", "xmlParseContent",
159"xmlParseElement", "xmlParseVersionNum", "xmlParseVersionInfo",
160"xmlParseEncName", "xmlParseEncodingDecl", "xmlParseSDDecl",
161"xmlParseXMLDecl", "xmlParseTextDecl", "xmlParseMisc",
162"xmlParseExternalSubset", "xmlParserHandlePEReference",
163"xmlSkipBlankChars",
164]
165
166#
167# These functions have side effects on the global state
168# and hence generate errors on memory allocation tests
169#
170skipped_memcheck = [ "xmlLoadCatalog", "xmlAddEncodingAlias",
171   "xmlSchemaInitTypes", "xmlNanoFTPProxy", "xmlNanoFTPScanProxy",
172   "xmlNanoHTTPScanProxy", "xmlResetLastError", "xmlCatalogConvert",
173   "xmlCatalogRemove", "xmlLoadCatalogs", "xmlCleanupCharEncodingHandlers",
174   "xmlInitCharEncodingHandlers", "xmlCatalogCleanup",
175   "xmlSchemaGetBuiltInType",
176   "htmlParseFile", "htmlCtxtReadFile", # loads the catalogs
177   "xmlTextReaderSchemaValidate", "xmlSchemaCleanupTypes", # initialize the schemas type system
178   "xmlCatalogResolve", "xmlIOParseDTD" # loads the catalogs
179]
180
181#
182# Extra code needed for some test cases
183#
184extra_pre_call = {
185   "xmlSAXUserParseFile": """
186#ifdef LIBXML_SAX1_ENABLED
187        if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
188#endif
189""",
190   "xmlSAXUserParseMemory": """
191#ifdef LIBXML_SAX1_ENABLED
192        if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
193#endif
194""",
195   "xmlParseBalancedChunkMemory": """
196#ifdef LIBXML_SAX1_ENABLED
197        if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
198#endif
199""",
200   "xmlParseBalancedChunkMemoryRecover": """
201#ifdef LIBXML_SAX1_ENABLED
202        if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL;
203#endif
204""",
205   "xmlParserInputBufferCreateFd":
206       "if (fd >= 0) fd = -1;",
207}
208extra_post_call = {
209   "xmlAddChild":
210       "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }",
211   "xmlAddEntity":
212       "if (ret_val != NULL) { xmlFreeNode(ret_val) ; ret_val = NULL; }",
213   "xmlAddChildList":
214       "if (ret_val == NULL) { xmlFreeNodeList(cur) ; cur = NULL ; }",
215   "xmlAddSibling":
216       "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
217   "xmlAddNextSibling":
218       "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
219   "xmlAddPrevSibling":
220       "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
221   "xmlDocSetRootElement":
222       "if (doc == NULL) { xmlFreeNode(root) ; root = NULL ; }",
223   "xmlReplaceNode":
224       """if (cur != NULL) {
225              xmlUnlinkNode(cur);
226              xmlFreeNode(cur) ; cur = NULL ; }
227          if (old != NULL) {
228              xmlUnlinkNode(old);
229              xmlFreeNode(old) ; old = NULL ; }
230	  ret_val = NULL;""",
231   "xmlTextMerge":
232       """if ((first != NULL) && (first->type != XML_TEXT_NODE)) {
233              xmlUnlinkNode(second);
234              xmlFreeNode(second) ; second = NULL ; }""",
235   "xmlBuildQName":
236       """if ((ret_val != NULL) && (ret_val != ncname) &&
237              (ret_val != prefix) && (ret_val != memory))
238              xmlFree(ret_val);
239	  ret_val = NULL;""",
240   "xmlNewDocElementContent":
241       """xmlFreeDocElementContent(doc, ret_val); ret_val = NULL;""",
242   "xmlDictReference": "xmlDictFree(dict);",
243   # Functions which deallocates one of their parameters
244   "xmlXPathConvertBoolean": """val = NULL;""",
245   "xmlXPathConvertNumber": """val = NULL;""",
246   "xmlXPathConvertString": """val = NULL;""",
247   "xmlSaveFileTo": """buf = NULL;""",
248   "xmlSaveFormatFileTo": """buf = NULL;""",
249   "xmlIOParseDTD": "input = NULL;",
250   "xmlRemoveProp": "cur = NULL;",
251   "xmlNewNs": "if ((node == NULL) && (ret_val != NULL)) xmlFreeNs(ret_val);",
252   "xmlCopyNamespace": "if (ret_val != NULL) xmlFreeNs(ret_val);",
253   "xmlCopyNamespaceList": "if (ret_val != NULL) xmlFreeNsList(ret_val);",
254   "xmlNewTextWriter": "if (ret_val != NULL) out = NULL;",
255   "xmlNewTextWriterPushParser": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;} if (ret_val != NULL) ctxt = NULL;",
256   "xmlNewIOInputStream": "if (ret_val != NULL) input = NULL;",
257   "htmlParseChunk": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
258   "htmlParseDocument": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
259   "xmlParseDocument": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
260   "xmlParseChunk": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
261   "xmlParseExtParsedEnt": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}",
262   "xmlDOMWrapAdoptNode": "if ((node != NULL) && (node->parent == NULL)) {xmlUnlinkNode(node);xmlFreeNode(node);node = NULL;}",
263   "xmlBufferSetAllocationScheme": "if ((buf != NULL) && (scheme == XML_BUFFER_ALLOC_IMMUTABLE) && (buf->content != NULL) && (buf->content != static_buf_content)) { xmlFree(buf->content); buf->content = NULL;}"
264}
265
266modules = []
267
268def is_skipped_module(name):
269    for mod in skipped_modules:
270        if mod == name:
271	    return 1
272    return 0
273
274def is_skipped_function(name):
275    for fun in skipped_functions:
276        if fun == name:
277	    return 1
278    # Do not test destructors
279    if string.find(name, 'Free') != -1:
280        return 1
281    return 0
282
283def is_skipped_memcheck(name):
284    for fun in skipped_memcheck:
285        if fun == name:
286	    return 1
287    return 0
288
289missing_types = {}
290def add_missing_type(name, func):
291    try:
292        list = missing_types[name]
293	list.append(func)
294    except:
295        missing_types[name] = [func]
296
297generated_param_types = []
298def add_generated_param_type(name):
299    generated_param_types.append(name)
300
301generated_return_types = []
302def add_generated_return_type(name):
303    generated_return_types.append(name)
304
305missing_functions = {}
306missing_functions_nr = 0
307def add_missing_functions(name, module):
308    global missing_functions_nr
309
310    missing_functions_nr = missing_functions_nr + 1
311    try:
312        list = missing_functions[module]
313	list.append(name)
314    except:
315        missing_functions[module] = [name]
316
317#
318# Provide the type generators and destructors for the parameters
319#
320
321def type_convert(str, name, info, module, function, pos):
322#    res = string.replace(str, "    ", " ")
323#    res = string.replace(str, "   ", " ")
324#    res = string.replace(str, "  ", " ")
325    res = string.replace(str, " *", "_ptr")
326#    res = string.replace(str, "*", "_ptr")
327    res = string.replace(res, " ", "_")
328    if res == 'const_char_ptr':
329        if string.find(name, "file") != -1 or \
330           string.find(name, "uri") != -1 or \
331           string.find(name, "URI") != -1 or \
332           string.find(info, "filename") != -1 or \
333           string.find(info, "URI") != -1 or \
334           string.find(info, "URL") != -1:
335	    if string.find(function, "Save") != -1 or \
336	       string.find(function, "Create") != -1 or \
337	       string.find(function, "Write") != -1 or \
338	       string.find(function, "Fetch") != -1:
339	        return('fileoutput')
340	    return('filepath')
341    if res == 'void_ptr':
342        if module == 'nanoftp' and name == 'ctx':
343	    return('xmlNanoFTPCtxtPtr')
344        if function == 'xmlNanoFTPNewCtxt' or \
345	   function == 'xmlNanoFTPConnectTo' or \
346	   function == 'xmlNanoFTPOpen':
347	    return('xmlNanoFTPCtxtPtr')
348        if module == 'nanohttp' and name == 'ctx':
349	    return('xmlNanoHTTPCtxtPtr')
350	if function == 'xmlNanoHTTPMethod' or \
351	   function == 'xmlNanoHTTPMethodRedir' or \
352	   function == 'xmlNanoHTTPOpen' or \
353	   function == 'xmlNanoHTTPOpenRedir':
354	    return('xmlNanoHTTPCtxtPtr');
355        if function == 'xmlIOHTTPOpen':
356	    return('xmlNanoHTTPCtxtPtr')
357	if string.find(name, "data") != -1:
358	    return('userdata')
359	if string.find(name, "user") != -1:
360	    return('userdata')
361    if res == 'xmlDoc_ptr':
362        res = 'xmlDocPtr'
363    if res == 'xmlNode_ptr':
364        res = 'xmlNodePtr'
365    if res == 'xmlDict_ptr':
366        res = 'xmlDictPtr'
367    if res == 'xmlNodePtr' and pos != 0:
368        if (function == 'xmlAddChild' and pos == 2) or \
369	   (function == 'xmlAddChildList' and pos == 2) or \
370           (function == 'xmlAddNextSibling' and pos == 2) or \
371           (function == 'xmlAddSibling' and pos == 2) or \
372           (function == 'xmlDocSetRootElement' and pos == 2) or \
373           (function == 'xmlReplaceNode' and pos == 2) or \
374           (function == 'xmlTextMerge') or \
375	   (function == 'xmlAddPrevSibling' and pos == 2):
376	    return('xmlNodePtr_in');
377    if res == 'const xmlBufferPtr':
378        res = 'xmlBufferPtr'
379    if res == 'xmlChar_ptr' and name == 'name' and \
380       string.find(function, "EatName") != -1:
381        return('eaten_name')
382    if res == 'void_ptr*':
383        res = 'void_ptr_ptr'
384    if res == 'char_ptr*':
385        res = 'char_ptr_ptr'
386    if res == 'xmlChar_ptr*':
387        res = 'xmlChar_ptr_ptr'
388    if res == 'const_xmlChar_ptr*':
389        res = 'const_xmlChar_ptr_ptr'
390    if res == 'const_char_ptr*':
391        res = 'const_char_ptr_ptr'
392    if res == 'FILE_ptr' and module == 'debugXML':
393        res = 'debug_FILE_ptr';
394    if res == 'int' and name == 'options':
395        if module == 'parser' or module == 'xmlreader':
396	    res = 'parseroptions'
397
398    return res
399
400known_param_types = []
401
402def is_known_param_type(name):
403    for type in known_param_types:
404        if type == name:
405	    return 1
406    return name[-3:] == 'Ptr' or name[-4:] == '_ptr'
407
408def generate_param_type(name, rtype):
409    global test
410    for type in known_param_types:
411        if type == name:
412	    return
413    for type in generated_param_types:
414        if type == name:
415	    return
416
417    if name[-3:] == 'Ptr' or name[-4:] == '_ptr':
418        if rtype[0:6] == 'const ':
419	    crtype = rtype[6:]
420	else:
421	    crtype = rtype
422
423        define = 0
424	if modules_defines.has_key(module):
425	    test.write("#ifdef %s\n" % (modules_defines[module]))
426	    define = 1
427        test.write("""
428#define gen_nb_%s 1
429static %s gen_%s(int no ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
430    return(NULL);
431}
432static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
433}
434""" % (name, crtype, name, name, rtype))
435        if define == 1:
436	    test.write("#endif\n\n")
437        add_generated_param_type(name)
438
439#
440# Provide the type destructors for the return values
441#
442
443known_return_types = []
444
445def is_known_return_type(name):
446    for type in known_return_types:
447        if type == name:
448	    return 1
449    return 0
450
451#
452# Copy the beginning of the C test program result
453#
454
455try:
456    input = open("testapi.c", "r")
457except:
458    input = open(srcPref + "testapi.c", "r")
459test = open('testapi.c.new', 'w')
460
461def compare_and_save():
462    global test
463
464    test.close()
465    try:
466        input = open("testapi.c", "r").read()
467    except:
468        input = ''
469    test = open('testapi.c.new', "r").read()
470    if input != test:
471        try:
472            os.system("rm testapi.c; mv testapi.c.new testapi.c")
473        except:
474	    os.system("mv testapi.c.new testapi.c")
475        print("Updated testapi.c")
476    else:
477        print("Generated testapi.c is identical")
478
479line = input.readline()
480while line != "":
481    if line == "/* CUT HERE: everything below that line is generated */\n":
482        break;
483    if line[0:15] == "#define gen_nb_":
484        type = string.split(line[15:])[0]
485	known_param_types.append(type)
486    if line[0:19] == "static void desret_":
487        type = string.split(line[19:], '(')[0]
488	known_return_types.append(type)
489    test.write(line)
490    line = input.readline()
491input.close()
492
493if line == "":
494    print "Could not find the CUT marker in testapi.c skipping generation"
495    test.close()
496    sys.exit(0)
497
498print("Scanned testapi.c: found %d parameters types and %d return types\n" % (
499      len(known_param_types), len(known_return_types)))
500test.write("/* CUT HERE: everything below that line is generated */\n")
501
502
503#
504# Open the input API description
505#
506doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0)
507if doc == None:
508    print "Failed to load doc/libxml2-api.xml"
509    sys.exit(1)
510ctxt = doc.xpathNewContext()
511
512#
513# Generate a list of all function parameters and select only
514# those used in the api tests
515#
516argtypes = {}
517args = ctxt.xpathEval("/api/symbols/function/arg")
518for arg in args:
519    mod = arg.xpathEval('string(../@file)')
520    func = arg.xpathEval('string(../@name)')
521    if (mod not in skipped_modules) and (func not in skipped_functions):
522	type = arg.xpathEval('string(@type)')
523	if not argtypes.has_key(type):
524	    argtypes[type] = func
525
526# similarly for return types
527rettypes = {}
528rets = ctxt.xpathEval("/api/symbols/function/return")
529for ret in rets:
530    mod = ret.xpathEval('string(../@file)')
531    func = ret.xpathEval('string(../@name)')
532    if (mod not in skipped_modules) and (func not in skipped_functions):
533        type = ret.xpathEval('string(@type)')
534	if not rettypes.has_key(type):
535	    rettypes[type] = func
536
537#
538# Generate constructors and return type handling for all enums
539# which are used as function parameters
540#
541enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']")
542for enum in enums:
543    module = enum.xpathEval('string(@file)')
544    name = enum.xpathEval('string(@name)')
545    #
546    # Skip any enums which are not in our filtered lists
547    #
548    if (name == None) or ((name not in argtypes) and (name not in rettypes)):
549        continue;
550    define = 0
551
552    if argtypes.has_key(name) and is_known_param_type(name) == 0:
553	values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name)
554	i = 0
555	vals = []
556	for value in values:
557	    vname = value.xpathEval('string(@name)')
558	    if vname == None:
559		continue;
560	    i = i + 1
561	    if i >= 5:
562		break;
563	    vals.append(vname)
564	if vals == []:
565	    print "Didn't find any value for enum %s" % (name)
566	    continue
567	if modules_defines.has_key(module):
568	    test.write("#ifdef %s\n" % (modules_defines[module]))
569	    define = 1
570	test.write("#define gen_nb_%s %d\n" % (name, len(vals)))
571	test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" %
572	           (name, name))
573	i = 1
574	for value in vals:
575	    test.write("    if (no == %d) return(%s);\n" % (i, value))
576	    i = i + 1
577	test.write("""    return(0);
578}
579
580static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
581}
582
583""" % (name, name));
584	known_param_types.append(name)
585
586    if (is_known_return_type(name) == 0) and (name in rettypes):
587	if define == 0 and modules_defines.has_key(module):
588	    test.write("#ifdef %s\n" % (modules_defines[module]))
589	    define = 1
590        test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) {
591}
592
593""" % (name, name))
594	known_return_types.append(name)
595    if define == 1:
596        test.write("#endif\n\n")
597
598#
599# Load the interfaces
600#
601headers = ctxt.xpathEval("/api/files/file")
602for file in headers:
603    name = file.xpathEval('string(@name)')
604    if (name == None) or (name == ''):
605        continue
606
607    #
608    # Some module may be skipped because they don't really consists
609    # of user callable APIs
610    #
611    if is_skipped_module(name):
612        continue
613
614    #
615    # do not test deprecated APIs
616    #
617    desc = file.xpathEval('string(description)')
618    if string.find(desc, 'DEPRECATED') != -1:
619        print "Skipping deprecated interface %s" % name
620	continue;
621
622    test.write("#include <libxml/%s.h>\n" % name)
623    modules.append(name)
624
625#
626# Generate the callers signatures
627#
628for module in modules:
629    test.write("static int test_%s(void);\n" % module);
630
631#
632# Generate the top caller
633#
634
635test.write("""
636/**
637 * testlibxml2:
638 *
639 * Main entry point of the tester for the full libxml2 module,
640 * it calls all the tester entry point for each module.
641 *
642 * Returns the number of error found
643 */
644static int
645testlibxml2(void)
646{
647    int test_ret = 0;
648
649""")
650
651for module in modules:
652    test.write("    test_ret += test_%s();\n" % module)
653
654test.write("""
655    printf("Total: %d functions, %d tests, %d errors\\n",
656           function_tests, call_tests, test_ret);
657    return(test_ret);
658}
659
660""")
661
662#
663# How to handle a function
664#
665nb_tests = 0
666
667def generate_test(module, node):
668    global test
669    global nb_tests
670    nb_cond = 0
671    no_gen = 0
672
673    name = node.xpathEval('string(@name)')
674    if is_skipped_function(name):
675        return
676
677    #
678    # check we know how to handle the args and return values
679    # and store the information for the generation
680    #
681    try:
682	args = node.xpathEval("arg")
683    except:
684        args = []
685    t_args = []
686    n = 0
687    for arg in args:
688        n = n + 1
689        rtype = arg.xpathEval("string(@type)")
690	if rtype == 'void':
691	    break;
692	info = arg.xpathEval("string(@info)")
693	nam = arg.xpathEval("string(@name)")
694        type = type_convert(rtype, nam, info, module, name, n)
695	if is_known_param_type(type) == 0:
696	    add_missing_type(type, name);
697	    no_gen = 1
698        if (type[-3:] == 'Ptr' or type[-4:] == '_ptr') and \
699	    rtype[0:6] == 'const ':
700	    crtype = rtype[6:]
701	else:
702	    crtype = rtype
703	t_args.append((nam, type, rtype, crtype, info))
704
705    try:
706	rets = node.xpathEval("return")
707    except:
708        rets = []
709    t_ret = None
710    for ret in rets:
711        rtype = ret.xpathEval("string(@type)")
712	info = ret.xpathEval("string(@info)")
713        type = type_convert(rtype, 'return', info, module, name, 0)
714	if rtype == 'void':
715	    break
716	if is_known_return_type(type) == 0:
717	    add_missing_type(type, name);
718	    no_gen = 1
719	t_ret = (type, rtype, info)
720	break
721
722    if no_gen == 0:
723        for t_arg in t_args:
724            (nam, type, rtype, crtype, info) = t_arg
725            generate_param_type(type, rtype)
726
727    test.write("""
728static int
729test_%s(void) {
730    int test_ret = 0;
731
732""" % (name))
733
734    if no_gen == 1:
735        add_missing_functions(name, module)
736	test.write("""
737    /* missing type support */
738    return(test_ret);
739}
740
741""")
742        return
743
744    try:
745	conds = node.xpathEval("cond")
746	for cond in conds:
747	    test.write("#if %s\n" % (cond.get_content()))
748	    nb_cond = nb_cond + 1
749    except:
750        pass
751
752    define = 0
753    if function_defines.has_key(name):
754        test.write("#ifdef %s\n" % (function_defines[name]))
755	define = 1
756
757    # Declare the memory usage counter
758    no_mem = is_skipped_memcheck(name)
759    if no_mem == 0:
760	test.write("    int mem_base;\n");
761
762    # Declare the return value
763    if t_ret != None:
764        test.write("    %s ret_val;\n" % (t_ret[1]))
765
766    # Declare the arguments
767    for arg in t_args:
768        (nam, type, rtype, crtype, info) = arg;
769	# add declaration
770	test.write("    %s %s; /* %s */\n" % (crtype, nam, info))
771	test.write("    int n_%s;\n" % (nam))
772    test.write("\n")
773
774    # Cascade loop on of each argument list of values
775    for arg in t_args:
776        (nam, type, rtype, crtype, info) = arg;
777	#
778	test.write("    for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % (
779	           nam, nam, type, nam))
780
781    # log the memory usage
782    if no_mem == 0:
783	test.write("        mem_base = xmlMemBlocks();\n");
784
785    # prepare the call
786    i = 0;
787    for arg in t_args:
788        (nam, type, rtype, crtype, info) = arg;
789	#
790	test.write("        %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i))
791	i = i + 1;
792
793    # add checks to avoid out-of-bounds array access
794    i = 0;
795    for arg in t_args:
796        (nam, type, rtype, crtype, info) = arg;
797        # assume that "size", "len", and "start" parameters apply to either
798        # the nearest preceding or following char pointer
799        if type == "int" and (nam == "size" or nam == "len" or nam == "start"):
800            for j in range(i - 1, -1, -1) + range(i + 1, len(t_args)):
801                (bnam, btype) = t_args[j][:2]
802                if btype == "const_char_ptr" or btype == "const_xmlChar_ptr":
803                    test.write(
804                        "        if ((%s != NULL) &&\n"
805                        "            (%s > (int) strlen((const char *) %s) + 1))\n"
806                        "            continue;\n"
807                        % (bnam, nam, bnam))
808                    break
809	i = i + 1;
810
811    # do the call, and clanup the result
812    if extra_pre_call.has_key(name):
813	test.write("        %s\n"% (extra_pre_call[name]))
814    if t_ret != None:
815	test.write("\n        ret_val = %s(" % (name))
816	need = 0
817	for arg in t_args:
818	    (nam, type, rtype, crtype, info) = arg
819	    if need:
820	        test.write(", ")
821	    else:
822	        need = 1
823	    if rtype != crtype:
824	        test.write("(%s)" % rtype)
825	    test.write("%s" % nam);
826	test.write(");\n")
827	if extra_post_call.has_key(name):
828	    test.write("        %s\n"% (extra_post_call[name]))
829	test.write("        desret_%s(ret_val);\n" % t_ret[0])
830    else:
831	test.write("\n        %s(" % (name));
832	need = 0;
833	for arg in t_args:
834	    (nam, type, rtype, crtype, info) = arg;
835	    if need:
836	        test.write(", ")
837	    else:
838	        need = 1
839	    if rtype != crtype:
840	        test.write("(%s)" % rtype)
841	    test.write("%s" % nam)
842	test.write(");\n")
843	if extra_post_call.has_key(name):
844	    test.write("        %s\n"% (extra_post_call[name]))
845
846    test.write("        call_tests++;\n");
847
848    # Free the arguments
849    i = 0;
850    for arg in t_args:
851        (nam, type, rtype, crtype, info) = arg;
852	# This is a hack to prevent generating a destructor for the
853	# 'input' argument in xmlTextReaderSetup.  There should be
854	# a better, more generic way to do this!
855	if string.find(info, 'destroy') == -1:
856	    test.write("        des_%s(n_%s, " % (type, nam))
857	    if rtype != crtype:
858	        test.write("(%s)" % rtype)
859	    test.write("%s, %d);\n" % (nam, i))
860	i = i + 1;
861
862    test.write("        xmlResetLastError();\n");
863    # Check the memory usage
864    if no_mem == 0:
865	test.write("""        if (mem_base != xmlMemBlocks()) {
866            printf("Leak of %%d blocks found in %s",
867	           xmlMemBlocks() - mem_base);
868	    test_ret++;
869""" % (name));
870	for arg in t_args:
871	    (nam, type, rtype, crtype, info) = arg;
872	    test.write("""            printf(" %%d", n_%s);\n""" % (nam))
873	test.write("""            printf("\\n");\n""")
874	test.write("        }\n")
875
876    for arg in t_args:
877	test.write("    }\n")
878
879    test.write("    function_tests++;\n")
880    #
881    # end of conditional
882    #
883    while nb_cond > 0:
884        test.write("#endif\n")
885	nb_cond = nb_cond -1
886    if define == 1:
887        test.write("#endif\n")
888
889    nb_tests = nb_tests + 1;
890
891    test.write("""
892    return(test_ret);
893}
894
895""")
896
897#
898# Generate all module callers
899#
900for module in modules:
901    # gather all the functions exported by that module
902    try:
903	functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
904    except:
905        print "Failed to gather functions from module %s" % (module)
906	continue;
907
908    # iterate over all functions in the module generating the test
909    i = 0
910    nb_tests_old = nb_tests
911    for function in functions:
912        i = i + 1
913        generate_test(module, function);
914
915    # header
916    test.write("""static int
917test_%s(void) {
918    int test_ret = 0;
919
920    if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n");
921""" % (module, module, nb_tests - nb_tests_old, i))
922
923    # iterate over all functions in the module generating the call
924    for function in functions:
925        name = function.xpathEval('string(@name)')
926	if is_skipped_function(name):
927	    continue
928	test.write("    test_ret += test_%s();\n" % (name))
929
930    # footer
931    test.write("""
932    if (test_ret != 0)
933	printf("Module %s: %%d errors\\n", test_ret);
934    return(test_ret);
935}
936""" % (module))
937
938#
939# Generate direct module caller
940#
941test.write("""static int
942test_module(const char *module) {
943""");
944for module in modules:
945    test.write("""    if (!strcmp(module, "%s")) return(test_%s());\n""" % (
946        module, module))
947test.write("""    return(0);
948}
949""");
950
951print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
952
953compare_and_save()
954
955missing_list = []
956for missing in missing_types.keys():
957    if missing == 'va_list' or missing == '...':
958        continue;
959
960    n = len(missing_types[missing])
961    missing_list.append((n, missing))
962
963def compare_missing(a, b):
964    return b[0] - a[0]
965
966missing_list.sort(compare_missing)
967print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))
968lst = open("missing.lst", "w")
969lst.write("Missing support for %d types" % (len(missing_list)))
970lst.write("\n")
971for miss in missing_list:
972    lst.write("%s: %d :" % (miss[1], miss[0]))
973    i = 0
974    for n in missing_types[miss[1]]:
975        i = i + 1
976        if i > 5:
977	    lst.write(" ...")
978	    break
979	lst.write(" %s" % (n))
980    lst.write("\n")
981lst.write("\n")
982lst.write("\n")
983lst.write("Missing support per module");
984for module in missing_functions.keys():
985    lst.write("module %s:\n   %s\n" % (module, missing_functions[module]))
986
987lst.close()
988
989
990