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