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