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    # do the call, and clanup the result
786    if extra_pre_call.has_key(name):
787	test.write("        %s\n"% (extra_pre_call[name]))
788    if t_ret != None:
789	test.write("\n        ret_val = %s(" % (name))
790	need = 0
791	for arg in t_args:
792	    (nam, type, rtype, crtype, info) = arg
793	    if need:
794	        test.write(", ")
795	    else:
796	        need = 1
797	    if rtype != crtype:
798	        test.write("(%s)" % rtype)
799	    test.write("%s" % nam);
800	test.write(");\n")
801	if extra_post_call.has_key(name):
802	    test.write("        %s\n"% (extra_post_call[name]))
803	test.write("        desret_%s(ret_val);\n" % t_ret[0])
804    else:
805	test.write("\n        %s(" % (name));
806	need = 0;
807	for arg in t_args:
808	    (nam, type, rtype, crtype, info) = arg;
809	    if need:
810	        test.write(", ")
811	    else:
812	        need = 1
813	    if rtype != crtype:
814	        test.write("(%s)" % rtype)
815	    test.write("%s" % nam)
816	test.write(");\n")
817	if extra_post_call.has_key(name):
818	    test.write("        %s\n"% (extra_post_call[name]))
819
820    test.write("        call_tests++;\n");
821
822    # Free the arguments
823    i = 0;
824    for arg in t_args:
825        (nam, type, rtype, crtype, info) = arg;
826	# This is a hack to prevent generating a destructor for the
827	# 'input' argument in xmlTextReaderSetup.  There should be
828	# a better, more generic way to do this!
829	if string.find(info, 'destroy') == -1:
830	    test.write("        des_%s(n_%s, " % (type, nam))
831	    if rtype != crtype:
832	        test.write("(%s)" % rtype)
833	    test.write("%s, %d);\n" % (nam, i))
834	i = i + 1;
835
836    test.write("        xmlResetLastError();\n");
837    # Check the memory usage
838    if no_mem == 0:
839	test.write("""        if (mem_base != xmlMemBlocks()) {
840            printf("Leak of %%d blocks found in %s",
841	           xmlMemBlocks() - mem_base);
842	    test_ret++;
843""" % (name));
844	for arg in t_args:
845	    (nam, type, rtype, crtype, info) = arg;
846	    test.write("""            printf(" %%d", n_%s);\n""" % (nam))
847	test.write("""            printf("\\n");\n""")
848	test.write("        }\n")
849
850    for arg in t_args:
851	test.write("    }\n")
852
853    test.write("    function_tests++;\n")
854    #
855    # end of conditional
856    #
857    while nb_cond > 0:
858        test.write("#endif\n")
859	nb_cond = nb_cond -1
860    if define == 1:
861        test.write("#endif\n")
862
863    nb_tests = nb_tests + 1;
864
865    test.write("""
866    return(test_ret);
867}
868
869""")
870
871#
872# Generate all module callers
873#
874for module in modules:
875    # gather all the functions exported by that module
876    try:
877	functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
878    except:
879        print "Failed to gather functions from module %s" % (module)
880	continue;
881
882    # iterate over all functions in the module generating the test
883    i = 0
884    nb_tests_old = nb_tests
885    for function in functions:
886        i = i + 1
887        generate_test(module, function);
888
889    # header
890    test.write("""static int
891test_%s(void) {
892    int test_ret = 0;
893
894    if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n");
895""" % (module, module, nb_tests - nb_tests_old, i))
896
897    # iterate over all functions in the module generating the call
898    for function in functions:
899        name = function.xpathEval('string(@name)')
900	if is_skipped_function(name):
901	    continue
902	test.write("    test_ret += test_%s();\n" % (name))
903
904    # footer
905    test.write("""
906    if (test_ret != 0)
907	printf("Module %s: %%d errors\\n", test_ret);
908    return(test_ret);
909}
910""" % (module))
911
912#
913# Generate direct module caller
914#
915test.write("""static int
916test_module(const char *module) {
917""");
918for module in modules:
919    test.write("""    if (!strcmp(module, "%s")) return(test_%s());\n""" % (
920        module, module))
921test.write("""    return(0);
922}
923""");
924
925print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
926
927compare_and_save()
928
929missing_list = []
930for missing in missing_types.keys():
931    if missing == 'va_list' or missing == '...':
932        continue;
933
934    n = len(missing_types[missing])
935    missing_list.append((n, missing))
936
937def compare_missing(a, b):
938    return b[0] - a[0]
939
940missing_list.sort(compare_missing)
941print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))
942lst = open("missing.lst", "w")
943lst.write("Missing support for %d types" % (len(missing_list)))
944lst.write("\n")
945for miss in missing_list:
946    lst.write("%s: %d :" % (miss[1], miss[0]))
947    i = 0
948    for n in missing_types[miss[1]]:
949        i = i + 1
950        if i > 5:
951	    lst.write(" ...")
952	    break
953	lst.write(" %s" % (n))
954    lst.write("\n")
955lst.write("\n")
956lst.write("\n")
957lst.write("Missing support per module");
958for module in missing_functions.keys():
959    lst.write("module %s:\n   %s\n" % (module, missing_functions[module]))
960
961lst.close()
962
963
964