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