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