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