• 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 informations 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, rtype):
403    global test
404    for type in known_param_types:
405        if type == name:
406	    return 1
407    for type in generated_param_types:
408        if type == name:
409	    return 1
410
411    if name[-3:] == 'Ptr' or name[-4:] == '_ptr':
412        if rtype[0:6] == 'const ':
413	    crtype = rtype[6:]
414	else:
415	    crtype = rtype
416
417        define = 0
418	if modules_defines.has_key(module):
419	    test.write("#ifdef %s\n" % (modules_defines[module]))
420	    define = 1
421        test.write("""
422#define gen_nb_%s 1
423static %s gen_%s(int no ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
424    return(NULL);
425}
426static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
427}
428""" % (name, crtype, name, name, rtype))
429        if define == 1:
430	    test.write("#endif\n\n")
431        add_generated_param_type(name)
432        return 1
433
434    return 0
435
436#
437# Provide the type destructors for the return values
438#
439
440known_return_types = []
441
442def is_known_return_type(name):
443    for type in known_return_types:
444        if type == name:
445	    return 1
446    return 0
447
448#
449# Copy the beginning of the C test program result
450#
451
452try:
453    input = open("testapi.c", "r")
454except:
455    input = open(srcPref + "testapi.c", "r")
456test = open('testapi.c.new', 'w')
457
458def compare_and_save():
459    global test
460
461    test.close()
462    try:
463        input = open("testapi.c", "r").read()
464    except:
465        input = ''
466    test = open('testapi.c.new', "r").read()
467    if input != test:
468        try:
469            os.system("rm testapi.c; mv testapi.c.new testapi.c")
470        except:
471	    os.system("mv testapi.c.new testapi.c")
472        print("Updated testapi.c")
473    else:
474        print("Generated testapi.c is identical")
475
476line = input.readline()
477while line != "":
478    if line == "/* CUT HERE: everything below that line is generated */\n":
479        break;
480    if line[0:15] == "#define gen_nb_":
481        type = string.split(line[15:])[0]
482	known_param_types.append(type)
483    if line[0:19] == "static void desret_":
484        type = string.split(line[19:], '(')[0]
485	known_return_types.append(type)
486    test.write(line)
487    line = input.readline()
488input.close()
489
490if line == "":
491    print "Could not find the CUT marker in testapi.c skipping generation"
492    test.close()
493    sys.exit(0)
494
495print("Scanned testapi.c: found %d parameters types and %d return types\n" % (
496      len(known_param_types), len(known_return_types)))
497test.write("/* CUT HERE: everything below that line is generated */\n")
498
499
500#
501# Open the input API description
502#
503doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0)
504if doc == None:
505    print "Failed to load doc/libxml2-api.xml"
506    sys.exit(1)
507ctxt = doc.xpathNewContext()
508
509#
510# Generate a list of all function parameters and select only
511# those used in the api tests
512#
513argtypes = {}
514args = ctxt.xpathEval("/api/symbols/function/arg")
515for arg in args:
516    mod = arg.xpathEval('string(../@file)')
517    func = arg.xpathEval('string(../@name)')
518    if (mod not in skipped_modules) and (func not in skipped_functions):
519	type = arg.xpathEval('string(@type)')
520	if not argtypes.has_key(type):
521	    argtypes[type] = func
522
523# similarly for return types
524rettypes = {}
525rets = ctxt.xpathEval("/api/symbols/function/return")
526for ret in rets:
527    mod = ret.xpathEval('string(../@file)')
528    func = ret.xpathEval('string(../@name)')
529    if (mod not in skipped_modules) and (func not in skipped_functions):
530        type = ret.xpathEval('string(@type)')
531	if not rettypes.has_key(type):
532	    rettypes[type] = func
533
534#
535# Generate constructors and return type handling for all enums
536# which are used as function parameters
537#
538enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']")
539for enum in enums:
540    module = enum.xpathEval('string(@file)')
541    name = enum.xpathEval('string(@name)')
542    #
543    # Skip any enums which are not in our filtered lists
544    #
545    if (name == None) or ((name not in argtypes) and (name not in rettypes)):
546        continue;
547    define = 0
548
549    if argtypes.has_key(name) and is_known_param_type(name, name) == 0:
550	values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name)
551	i = 0
552	vals = []
553	for value in values:
554	    vname = value.xpathEval('string(@name)')
555	    if vname == None:
556		continue;
557	    i = i + 1
558	    if i >= 5:
559		break;
560	    vals.append(vname)
561	if vals == []:
562	    print "Didn't find any value for enum %s" % (name)
563	    continue
564	if modules_defines.has_key(module):
565	    test.write("#ifdef %s\n" % (modules_defines[module]))
566	    define = 1
567	test.write("#define gen_nb_%s %d\n" % (name, len(vals)))
568	test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" %
569	           (name, name))
570	i = 1
571	for value in vals:
572	    test.write("    if (no == %d) return(%s);\n" % (i, value))
573	    i = i + 1
574	test.write("""    return(0);
575}
576
577static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
578}
579
580""" % (name, name));
581	known_param_types.append(name)
582
583    if (is_known_return_type(name) == 0) and (name in rettypes):
584	if define == 0 and modules_defines.has_key(module):
585	    test.write("#ifdef %s\n" % (modules_defines[module]))
586	    define = 1
587        test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) {
588}
589
590""" % (name, name))
591	known_return_types.append(name)
592    if define == 1:
593        test.write("#endif\n\n")
594
595#
596# Load the interfaces
597#
598headers = ctxt.xpathEval("/api/files/file")
599for file in headers:
600    name = file.xpathEval('string(@name)')
601    if (name == None) or (name == ''):
602        continue
603
604    #
605    # Some module may be skipped because they don't really consists
606    # of user callable APIs
607    #
608    if is_skipped_module(name):
609        continue
610
611    #
612    # do not test deprecated APIs
613    #
614    desc = file.xpathEval('string(description)')
615    if string.find(desc, 'DEPRECATED') != -1:
616        print "Skipping deprecated interface %s" % name
617	continue;
618
619    test.write("#include <libxml/%s.h>\n" % name)
620    modules.append(name)
621
622#
623# Generate the callers signatures
624#
625for module in modules:
626    test.write("static int test_%s(void);\n" % module);
627
628#
629# Generate the top caller
630#
631
632test.write("""
633/**
634 * testlibxml2:
635 *
636 * Main entry point of the tester for the full libxml2 module,
637 * it calls all the tester entry point for each module.
638 *
639 * Returns the number of error found
640 */
641static int
642testlibxml2(void)
643{
644    int test_ret = 0;
645
646""")
647
648for module in modules:
649    test.write("    test_ret += test_%s();\n" % module)
650
651test.write("""
652    printf("Total: %d functions, %d tests, %d errors\\n",
653           function_tests, call_tests, test_ret);
654    return(test_ret);
655}
656
657""")
658
659#
660# How to handle a function
661#
662nb_tests = 0
663
664def generate_test(module, node):
665    global test
666    global nb_tests
667    nb_cond = 0
668    no_gen = 0
669
670    name = node.xpathEval('string(@name)')
671    if is_skipped_function(name):
672        return
673
674    #
675    # check we know how to handle the args and return values
676    # and store the informations for the generation
677    #
678    try:
679	args = node.xpathEval("arg")
680    except:
681        args = []
682    t_args = []
683    n = 0
684    for arg in args:
685        n = n + 1
686        rtype = arg.xpathEval("string(@type)")
687	if rtype == 'void':
688	    break;
689	info = arg.xpathEval("string(@info)")
690	nam = arg.xpathEval("string(@name)")
691        type = type_convert(rtype, nam, info, module, name, n)
692	if is_known_param_type(type, rtype) == 0:
693	    add_missing_type(type, name);
694	    no_gen = 1
695        if (type[-3:] == 'Ptr' or type[-4:] == '_ptr') and \
696	    rtype[0:6] == 'const ':
697	    crtype = rtype[6:]
698	else:
699	    crtype = rtype
700	t_args.append((nam, type, rtype, crtype, info))
701
702    try:
703	rets = node.xpathEval("return")
704    except:
705        rets = []
706    t_ret = None
707    for ret in rets:
708        rtype = ret.xpathEval("string(@type)")
709	info = ret.xpathEval("string(@info)")
710        type = type_convert(rtype, 'return', info, module, name, 0)
711	if rtype == 'void':
712	    break
713	if is_known_return_type(type) == 0:
714	    add_missing_type(type, name);
715	    no_gen = 1
716	t_ret = (type, rtype, info)
717	break
718
719    test.write("""
720static int
721test_%s(void) {
722    int test_ret = 0;
723
724""" % (name))
725
726    if no_gen == 1:
727        add_missing_functions(name, module)
728	test.write("""
729    /* missing type support */
730    return(test_ret);
731}
732
733""")
734        return
735
736    try:
737	conds = node.xpathEval("cond")
738	for cond in conds:
739	    test.write("#if %s\n" % (cond.get_content()))
740	    nb_cond = nb_cond + 1
741    except:
742        pass
743
744    define = 0
745    if function_defines.has_key(name):
746        test.write("#ifdef %s\n" % (function_defines[name]))
747	define = 1
748
749    # Declare the memory usage counter
750    no_mem = is_skipped_memcheck(name)
751    if no_mem == 0:
752	test.write("    int mem_base;\n");
753
754    # Declare the return value
755    if t_ret != None:
756        test.write("    %s ret_val;\n" % (t_ret[1]))
757
758    # Declare the arguments
759    for arg in t_args:
760        (nam, type, rtype, crtype, info) = arg;
761	# add declaration
762	test.write("    %s %s; /* %s */\n" % (crtype, nam, info))
763	test.write("    int n_%s;\n" % (nam))
764    test.write("\n")
765
766    # Cascade loop on of each argument list of values
767    for arg in t_args:
768        (nam, type, rtype, crtype, info) = arg;
769	#
770	test.write("    for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % (
771	           nam, nam, type, nam))
772
773    # log the memory usage
774    if no_mem == 0:
775	test.write("        mem_base = xmlMemBlocks();\n");
776
777    # prepare the call
778    i = 0;
779    for arg in t_args:
780        (nam, type, rtype, crtype, info) = arg;
781	#
782	test.write("        %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i))
783	i = i + 1;
784
785    # add checks to avoid out-of-bounds array access
786    i = 0;
787    for arg in t_args:
788        (nam, type, rtype, crtype, info) = arg;
789        # assume that "size", "len", and "start" parameters apply to either
790        # the nearest preceding or following char pointer
791        if type == "int" and (nam == "size" or nam == "len" or nam == "start"):
792            for j in range(i - 1, -1, -1) + range(i + 1, len(t_args)):
793                (bnam, btype) = t_args[j][:2]
794                if btype == "const_char_ptr" or btype == "const_xmlChar_ptr":
795                    test.write(
796                        "        if ((%s != NULL) &&\n"
797                        "            (%s > (int) strlen((const char *) %s) + 1))\n"
798                        "            continue;\n"
799                        % (bnam, nam, bnam))
800                    break
801	i = i + 1;
802
803    # do the call, and clanup the result
804    if extra_pre_call.has_key(name):
805	test.write("        %s\n"% (extra_pre_call[name]))
806    if t_ret != None:
807	test.write("\n        ret_val = %s(" % (name))
808	need = 0
809	for arg in t_args:
810	    (nam, type, rtype, crtype, info) = arg
811	    if need:
812	        test.write(", ")
813	    else:
814	        need = 1
815	    if rtype != crtype:
816	        test.write("(%s)" % rtype)
817	    test.write("%s" % nam);
818	test.write(");\n")
819	if extra_post_call.has_key(name):
820	    test.write("        %s\n"% (extra_post_call[name]))
821	test.write("        desret_%s(ret_val);\n" % t_ret[0])
822    else:
823	test.write("\n        %s(" % (name));
824	need = 0;
825	for arg in t_args:
826	    (nam, type, rtype, crtype, info) = arg;
827	    if need:
828	        test.write(", ")
829	    else:
830	        need = 1
831	    if rtype != crtype:
832	        test.write("(%s)" % rtype)
833	    test.write("%s" % nam)
834	test.write(");\n")
835	if extra_post_call.has_key(name):
836	    test.write("        %s\n"% (extra_post_call[name]))
837
838    test.write("        call_tests++;\n");
839
840    # Free the arguments
841    i = 0;
842    for arg in t_args:
843        (nam, type, rtype, crtype, info) = arg;
844	# This is a hack to prevent generating a destructor for the
845	# 'input' argument in xmlTextReaderSetup.  There should be
846	# a better, more generic way to do this!
847	if string.find(info, 'destroy') == -1:
848	    test.write("        des_%s(n_%s, " % (type, nam))
849	    if rtype != crtype:
850	        test.write("(%s)" % rtype)
851	    test.write("%s, %d);\n" % (nam, i))
852	i = i + 1;
853
854    test.write("        xmlResetLastError();\n");
855    # Check the memory usage
856    if no_mem == 0:
857	test.write("""        if (mem_base != xmlMemBlocks()) {
858            printf("Leak of %%d blocks found in %s",
859	           xmlMemBlocks() - mem_base);
860	    test_ret++;
861""" % (name));
862	for arg in t_args:
863	    (nam, type, rtype, crtype, info) = arg;
864	    test.write("""            printf(" %%d", n_%s);\n""" % (nam))
865	test.write("""            printf("\\n");\n""")
866	test.write("        }\n")
867
868    for arg in t_args:
869	test.write("    }\n")
870
871    test.write("    function_tests++;\n")
872    #
873    # end of conditional
874    #
875    while nb_cond > 0:
876        test.write("#endif\n")
877	nb_cond = nb_cond -1
878    if define == 1:
879        test.write("#endif\n")
880
881    nb_tests = nb_tests + 1;
882
883    test.write("""
884    return(test_ret);
885}
886
887""")
888
889#
890# Generate all module callers
891#
892for module in modules:
893    # gather all the functions exported by that module
894    try:
895	functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
896    except:
897        print "Failed to gather functions from module %s" % (module)
898	continue;
899
900    # iterate over all functions in the module generating the test
901    i = 0
902    nb_tests_old = nb_tests
903    for function in functions:
904        i = i + 1
905        generate_test(module, function);
906
907    # header
908    test.write("""static int
909test_%s(void) {
910    int test_ret = 0;
911
912    if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n");
913""" % (module, module, nb_tests - nb_tests_old, i))
914
915    # iterate over all functions in the module generating the call
916    for function in functions:
917        name = function.xpathEval('string(@name)')
918	if is_skipped_function(name):
919	    continue
920	test.write("    test_ret += test_%s();\n" % (name))
921
922    # footer
923    test.write("""
924    if (test_ret != 0)
925	printf("Module %s: %%d errors\\n", test_ret);
926    return(test_ret);
927}
928""" % (module))
929
930#
931# Generate direct module caller
932#
933test.write("""static int
934test_module(const char *module) {
935""");
936for module in modules:
937    test.write("""    if (!strcmp(module, "%s")) return(test_%s());\n""" % (
938        module, module))
939test.write("""    return(0);
940}
941""");
942
943print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
944
945compare_and_save()
946
947missing_list = []
948for missing in missing_types.keys():
949    if missing == 'va_list' or missing == '...':
950        continue;
951
952    n = len(missing_types[missing])
953    missing_list.append((n, missing))
954
955def compare_missing(a, b):
956    return b[0] - a[0]
957
958missing_list.sort(compare_missing)
959print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))
960lst = open("missing.lst", "w")
961lst.write("Missing support for %d types" % (len(missing_list)))
962lst.write("\n")
963for miss in missing_list:
964    lst.write("%s: %d :" % (miss[1], miss[0]))
965    i = 0
966    for n in missing_types[miss[1]]:
967        i = i + 1
968        if i > 5:
969	    lst.write(" ...")
970	    break
971	lst.write(" %s" % (n))
972    lst.write("\n")
973lst.write("\n")
974lst.write("\n")
975lst.write("Missing support per module");
976for module in missing_functions.keys():
977    lst.write("module %s:\n   %s\n" % (module, missing_functions[module]))
978
979lst.close()
980
981
982