• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
982