Home | History | Annotate | Download | only in libxml2
      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