• 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",
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        if rtype[0:6] == 'const ':
427            crtype = rtype[6:]
428        else:
429            crtype = rtype
430
431        define = 0
432        if module in modules_defines:
433            test.write("#ifdef %s\n" % (modules_defines[module]))
434            define = 1
435        test.write("""
436#define gen_nb_%s 1
437#define gen_%s(no, nr) NULL
438#define des_%s(no, val, nr)
439""" % (name, name, name))
440        if define == 1:
441            test.write("#endif\n\n")
442        add_generated_param_type(name)
443
444#
445# Provide the type destructors for the return values
446#
447
448known_return_types = []
449
450def is_known_return_type(name):
451    for type in known_return_types:
452        if type == name:
453            return 1
454    return 0
455
456#
457# Copy the beginning of the C test program result
458#
459
460try:
461    input = open("testapi.c", "r")
462except:
463    input = open(srcPref + "testapi.c", "r")
464test = open('testapi.c.new', 'w')
465
466def compare_and_save():
467    global test
468
469    test.close()
470    try:
471        input = open("testapi.c", "r").read()
472    except:
473        input = ''
474    test = open('testapi.c.new', "r").read()
475    if input != test:
476        try:
477            os.system("rm testapi.c; mv testapi.c.new testapi.c")
478        except:
479            os.system("mv testapi.c.new testapi.c")
480        print("Updated testapi.c")
481    else:
482        print("Generated testapi.c is identical")
483
484line = input.readline()
485while line != "":
486    if line == "/* CUT HERE: everything below that line is generated */\n":
487        break;
488    if line[0:15] == "#define gen_nb_":
489        type = line[15:].split()[0]
490        known_param_types.append(type)
491    if line[0:19] == "static void desret_":
492        type = line[19:].split('(')[0]
493        known_return_types.append(type)
494    test.write(line)
495    line = input.readline()
496input.close()
497
498if line == "":
499    print("Could not find the CUT marker in testapi.c skipping generation")
500    test.close()
501    sys.exit(0)
502
503print("Scanned testapi.c: found %d parameters types and %d return types\n" % (
504      len(known_param_types), len(known_return_types)))
505test.write("/* CUT HERE: everything below that line is generated */\n")
506
507
508#
509# Open the input API description
510#
511doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0)
512if doc == None:
513    print("Failed to load doc/libxml2-api.xml")
514    sys.exit(1)
515ctxt = doc.xpathNewContext()
516
517#
518# Generate a list of all function parameters and select only
519# those used in the api tests
520#
521argtypes = {}
522args = ctxt.xpathEval("/api/symbols/function/arg")
523for arg in args:
524    mod = arg.xpathEval('string(../@file)')
525    func = arg.xpathEval('string(../@name)')
526    if (mod not in skipped_modules) and (func not in skipped_functions):
527        type = arg.xpathEval('string(@type)')
528        if type not in argtypes:
529            argtypes[type] = func
530
531# similarly for return types
532rettypes = {}
533rets = ctxt.xpathEval("/api/symbols/function/return")
534for ret in rets:
535    mod = ret.xpathEval('string(../@file)')
536    func = ret.xpathEval('string(../@name)')
537    if (mod not in skipped_modules) and (func not in skipped_functions):
538        type = ret.xpathEval('string(@type)')
539        if type not in rettypes:
540            rettypes[type] = func
541
542#
543# Generate constructors and return type handling for all enums
544# which are used as function parameters
545#
546enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']")
547for enum in enums:
548    module = enum.xpathEval('string(@file)')
549    name = enum.xpathEval('string(@name)')
550    #
551    # Skip any enums which are not in our filtered lists
552    #
553    if (name == None) or ((name not in argtypes) and (name not in rettypes)):
554        continue;
555    define = 0
556
557    if (name in argtypes) and is_known_param_type(name) == 0:
558        values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name)
559        i = 0
560        vals = []
561        for value in values:
562            vname = value.xpathEval('string(@name)')
563            if vname == None:
564                continue;
565            i = i + 1
566            if i >= 5:
567                break;
568            vals.append(vname)
569        if vals == []:
570            print("Didn't find any value for enum %s" % (name))
571            continue
572        if module in modules_defines:
573            test.write("#ifdef %s\n" % (modules_defines[module]))
574            define = 1
575        test.write("#define gen_nb_%s %d\n" % (name, len(vals)))
576        test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" %
577                   (name, name))
578        i = 1
579        for value in vals:
580            test.write("    if (no == %d) return(%s);\n" % (i, value))
581            i = i + 1
582        test.write("""    return(0);
583}
584
585static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) {
586}
587
588""" % (name, name));
589        known_param_types.append(name)
590
591    if (is_known_return_type(name) == 0) and (name in rettypes):
592        if define == 0 and (module in modules_defines):
593            test.write("#ifdef %s\n" % (modules_defines[module]))
594            define = 1
595        test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) {
596}
597
598""" % (name, name))
599        known_return_types.append(name)
600    if define == 1:
601        test.write("#endif\n\n")
602
603#
604# Load the interfaces
605#
606headers = ctxt.xpathEval("/api/files/file")
607for file in headers:
608    name = file.xpathEval('string(@name)')
609    if (name == None) or (name == ''):
610        continue
611
612    #
613    # Some module may be skipped because they don't really consists
614    # of user callable APIs
615    #
616    if is_skipped_module(name):
617        continue
618
619    #
620    # do not test deprecated APIs
621    #
622    desc = file.xpathEval('string(description)')
623    if desc.find('DEPRECATED') != -1:
624        print("Skipping deprecated interface %s" % name)
625        continue;
626
627    test.write("#include <libxml/%s.h>\n" % name)
628    modules.append(name)
629
630#
631# Generate the callers signatures
632#
633for module in modules:
634    test.write("static int test_%s(void);\n" % module);
635
636#
637# Generate the top caller
638#
639
640test.write("""
641/**
642 * testlibxml2:
643 *
644 * Main entry point of the tester for the full libxml2 module,
645 * it calls all the tester entry point for each module.
646 *
647 * Returns the number of error found
648 */
649static int
650testlibxml2(void)
651{
652    int test_ret = 0;
653
654""")
655
656for module in modules:
657    test.write("    test_ret += test_%s();\n" % module)
658
659test.write("""
660    printf("Total: %d functions, %d tests, %d errors\\n",
661           function_tests, call_tests, test_ret);
662    return(test_ret);
663}
664
665""")
666
667#
668# How to handle a function
669#
670nb_tests = 0
671
672def generate_test(module, node):
673    global test
674    global nb_tests
675    nb_cond = 0
676    no_gen = 0
677
678    name = node.xpathEval('string(@name)')
679    if is_skipped_function(name):
680        return
681
682    #
683    # check we know how to handle the args and return values
684    # and store the information for the generation
685    #
686    try:
687        args = node.xpathEval("arg")
688    except:
689        args = []
690    t_args = []
691    n = 0
692    for arg in args:
693        n = n + 1
694        rtype = arg.xpathEval("string(@type)")
695        if rtype == 'void':
696            break;
697        info = arg.xpathEval("string(@info)")
698        nam = arg.xpathEval("string(@name)")
699        type = type_convert(rtype, nam, info, module, name, n)
700        if is_known_param_type(type) == 0:
701            add_missing_type(type, name);
702            no_gen = 1
703        if (type[-3:] == 'Ptr' or type[-4:] == '_ptr') and \
704            rtype[0:6] == 'const ':
705            crtype = rtype[6:]
706        else:
707            crtype = rtype
708        t_args.append((nam, type, rtype, crtype, info))
709
710    try:
711        rets = node.xpathEval("return")
712    except:
713        rets = []
714    t_ret = None
715    for ret in rets:
716        rtype = ret.xpathEval("string(@type)")
717        info = ret.xpathEval("string(@info)")
718        type = type_convert(rtype, 'return', info, module, name, 0)
719        if rtype == 'void':
720            break
721        if is_known_return_type(type) == 0:
722            add_missing_type(type, name);
723            no_gen = 1
724        t_ret = (type, rtype, info)
725        break
726
727    if no_gen == 0:
728        for t_arg in t_args:
729            (nam, type, rtype, crtype, info) = t_arg
730            generate_param_type(type, rtype)
731
732    test.write("""
733static int
734test_%s(void) {
735    int test_ret = 0;
736
737""" % (name))
738
739    if no_gen == 1:
740        add_missing_functions(name, module)
741        test.write("""
742    /* missing type support */
743    return(test_ret);
744}
745
746""")
747        return
748
749    try:
750        conds = node.xpathEval("cond")
751        for cond in conds:
752            test.write("#if %s\n" % (cond.get_content()))
753            nb_cond = nb_cond + 1
754    except:
755        pass
756
757    define = 0
758    if name in function_defines:
759        test.write("#ifdef %s\n" % (function_defines[name]))
760        define = 1
761
762    # Declare the memory usage counter
763    no_mem = is_skipped_memcheck(name)
764    if no_mem == 0:
765        test.write("    int mem_base;\n");
766
767    # Declare the return value
768    if t_ret != None:
769        test.write("    %s ret_val;\n" % (t_ret[1]))
770
771    # Declare the arguments
772    for arg in t_args:
773        (nam, type, rtype, crtype, info) = arg;
774        # add declaration
775        test.write("    %s %s; /* %s */\n" % (crtype, nam, info))
776        test.write("    int n_%s;\n" % (nam))
777    test.write("\n")
778
779    # Cascade loop on of each argument list of values
780    for arg in t_args:
781        (nam, type, rtype, crtype, info) = arg;
782        #
783        test.write("    for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % (
784                   nam, nam, type, nam))
785
786    # log the memory usage
787    if no_mem == 0:
788        test.write("        mem_base = xmlMemBlocks();\n");
789
790    # prepare the call
791    i = 0;
792    for arg in t_args:
793        (nam, type, rtype, crtype, info) = arg;
794        #
795        test.write("        %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i))
796        i = i + 1;
797
798    # add checks to avoid out-of-bounds array access
799    i = 0;
800    for arg in t_args:
801        (nam, type, rtype, crtype, info) = arg;
802        # assume that "size", "len", and "start" parameters apply to either
803        # the nearest preceding or following char pointer
804        if type == "int" and (nam == "size" or nam == "len" or nam == "start"):
805            for j in (list(range(i - 1, -1, -1)) + list(range(i + 1, len(t_args)))):
806                (bnam, btype) = t_args[j][:2]
807                if btype == "const_char_ptr" or btype == "const_xmlChar_ptr":
808                    test.write(
809                        "        if ((%s != NULL) &&\n"
810                        "            (%s > xmlStrlen(BAD_CAST %s)))\n"
811                        "            %s = 0;\n"
812                        % (bnam, nam, bnam, nam))
813                    break
814        i = i + 1;
815
816    # do the call, and clanup the result
817    if name in extra_pre_call:
818        test.write("        %s\n"% (extra_pre_call[name]))
819    if t_ret != None:
820        test.write("\n        ret_val = %s(" % (name))
821        need = 0
822        for arg in t_args:
823            (nam, type, rtype, crtype, info) = arg
824            if need:
825                test.write(", ")
826            else:
827                need = 1
828            if rtype != crtype:
829                test.write("(%s)" % rtype)
830            test.write("%s" % nam);
831        test.write(");\n")
832        if name in extra_post_call:
833            test.write("        %s\n"% (extra_post_call[name]))
834        test.write("        desret_%s(ret_val);\n" % t_ret[0])
835    else:
836        test.write("\n        %s(" % (name));
837        need = 0;
838        for arg in t_args:
839            (nam, type, rtype, crtype, info) = arg;
840            if need:
841                test.write(", ")
842            else:
843                need = 1
844            if rtype != crtype:
845                test.write("(%s)" % rtype)
846            test.write("%s" % nam)
847        test.write(");\n")
848        if name in extra_post_call:
849            test.write("        %s\n"% (extra_post_call[name]))
850
851    test.write("        call_tests++;\n");
852
853    # Free the arguments
854    i = 0;
855    for arg in t_args:
856        (nam, type, rtype, crtype, info) = arg;
857        # This is a hack to prevent generating a destructor for the
858        # 'input' argument in xmlTextReaderSetup.  There should be
859        # a better, more generic way to do this!
860        if info.find('destroy') == -1:
861            test.write("        des_%s(n_%s, " % (type, nam))
862            if rtype != crtype:
863                test.write("(%s)" % rtype)
864            test.write("%s, %d);\n" % (nam, i))
865        i = i + 1;
866
867    test.write("        xmlResetLastError();\n");
868    # Check the memory usage
869    if no_mem == 0:
870        test.write("""        if (mem_base != xmlMemBlocks()) {
871            printf("Leak of %%d blocks found in %s",
872\t           xmlMemBlocks() - mem_base);
873\t    test_ret++;
874""" % (name));
875        for arg in t_args:
876            (nam, type, rtype, crtype, info) = arg;
877            test.write("""            printf(" %%d", n_%s);\n""" % (nam))
878        test.write("""            printf("\\n");\n""")
879        test.write("        }\n")
880
881    for arg in t_args:
882        test.write("    }\n")
883
884    test.write("    function_tests++;\n")
885    #
886    # end of conditional
887    #
888    while nb_cond > 0:
889        test.write("#endif\n")
890        nb_cond = nb_cond -1
891    if define == 1:
892        test.write("#endif\n")
893
894    nb_tests = nb_tests + 1;
895
896    test.write("""
897    return(test_ret);
898}
899
900""")
901
902#
903# Generate all module callers
904#
905for module in modules:
906    # gather all the functions exported by that module
907    try:
908        functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
909    except:
910        print("Failed to gather functions from module %s" % (module))
911        continue;
912
913    # iterate over all functions in the module generating the test
914    i = 0
915    nb_tests_old = nb_tests
916    for function in functions:
917        i = i + 1
918        generate_test(module, function);
919
920    # header
921    test.write("""static int
922test_%s(void) {
923    int test_ret = 0;
924
925    if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n");
926""" % (module, module, nb_tests - nb_tests_old, i))
927
928    # iterate over all functions in the module generating the call
929    for function in functions:
930        name = function.xpathEval('string(@name)')
931        if is_skipped_function(name):
932            continue
933        test.write("    test_ret += test_%s();\n" % (name))
934
935    # footer
936    test.write("""
937    if (test_ret != 0)
938\tprintf("Module %s: %%d errors\\n", test_ret);
939    return(test_ret);
940}
941""" % (module))
942
943#
944# Generate direct module caller
945#
946test.write("""static int
947test_module(const char *module) {
948""");
949for module in modules:
950    test.write("""    if (!strcmp(module, "%s")) return(test_%s());\n""" % (
951        module, module))
952test.write("""    return(0);
953}
954""");
955
956print("Generated test for %d modules and %d functions" %(len(modules), nb_tests))
957
958compare_and_save()
959
960missing_list = []
961for missing in missing_types.keys():
962    if missing == 'va_list' or missing == '...':
963        continue;
964
965    n = len(missing_types[missing])
966    missing_list.append((n, missing))
967
968missing_list.sort(key=lambda a: a[0])
969print("Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list)))
970lst = open("missing.lst", "w")
971lst.write("Missing support for %d types" % (len(missing_list)))
972lst.write("\n")
973for miss in missing_list:
974    lst.write("%s: %d :" % (miss[1], miss[0]))
975    i = 0
976    for n in missing_types[miss[1]]:
977        i = i + 1
978        if i > 5:
979            lst.write(" ...")
980            break
981        lst.write(" %s" % (n))
982    lst.write("\n")
983lst.write("\n")
984lst.write("\n")
985lst.write("Missing support per module");
986for module in missing_functions.keys():
987    lst.write("module %s:\n   %s\n" % (module, missing_functions[module]))
988
989lst.close()
990
991
992