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     # do the call, and clanup the result
    786     if extra_pre_call.has_key(name):
    787 	test.write("        %s\n"% (extra_pre_call[name]))
    788     if t_ret != None:
    789 	test.write("\n        ret_val = %s(" % (name))
    790 	need = 0
    791 	for arg in t_args:
    792 	    (nam, type, rtype, crtype, info) = arg
    793 	    if need:
    794 	        test.write(", ")
    795 	    else:
    796 	        need = 1
    797 	    if rtype != crtype:
    798 	        test.write("(%s)" % rtype)
    799 	    test.write("%s" % nam);
    800 	test.write(");\n")
    801 	if extra_post_call.has_key(name):
    802 	    test.write("        %s\n"% (extra_post_call[name]))
    803 	test.write("        desret_%s(ret_val);\n" % t_ret[0])
    804     else:
    805 	test.write("\n        %s(" % (name));
    806 	need = 0;
    807 	for arg in t_args:
    808 	    (nam, type, rtype, crtype, info) = arg;
    809 	    if need:
    810 	        test.write(", ")
    811 	    else:
    812 	        need = 1
    813 	    if rtype != crtype:
    814 	        test.write("(%s)" % rtype)
    815 	    test.write("%s" % nam)
    816 	test.write(");\n")
    817 	if extra_post_call.has_key(name):
    818 	    test.write("        %s\n"% (extra_post_call[name]))
    819 
    820     test.write("        call_tests++;\n");
    821 
    822     # Free the arguments
    823     i = 0;
    824     for arg in t_args:
    825         (nam, type, rtype, crtype, info) = arg;
    826 	# This is a hack to prevent generating a destructor for the
    827 	# 'input' argument in xmlTextReaderSetup.  There should be
    828 	# a better, more generic way to do this!
    829 	if string.find(info, 'destroy') == -1:
    830 	    test.write("        des_%s(n_%s, " % (type, nam))
    831 	    if rtype != crtype:
    832 	        test.write("(%s)" % rtype)
    833 	    test.write("%s, %d);\n" % (nam, i))
    834 	i = i + 1;
    835 
    836     test.write("        xmlResetLastError();\n");
    837     # Check the memory usage
    838     if no_mem == 0:
    839 	test.write("""        if (mem_base != xmlMemBlocks()) {
    840             printf("Leak of %%d blocks found in %s",
    841 	           xmlMemBlocks() - mem_base);
    842 	    test_ret++;
    843 """ % (name));
    844 	for arg in t_args:
    845 	    (nam, type, rtype, crtype, info) = arg;
    846 	    test.write("""            printf(" %%d", n_%s);\n""" % (nam))
    847 	test.write("""            printf("\\n");\n""")
    848 	test.write("        }\n")
    849 
    850     for arg in t_args:
    851 	test.write("    }\n")
    852 
    853     test.write("    function_tests++;\n")
    854     #
    855     # end of conditional
    856     #
    857     while nb_cond > 0:
    858         test.write("#endif\n")
    859 	nb_cond = nb_cond -1
    860     if define == 1:
    861         test.write("#endif\n")
    862 
    863     nb_tests = nb_tests + 1;
    864 
    865     test.write("""
    866     return(test_ret);
    867 }
    868 
    869 """)
    870     
    871 #
    872 # Generate all module callers
    873 #
    874 for module in modules:
    875     # gather all the functions exported by that module
    876     try:
    877 	functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
    878     except:
    879         print "Failed to gather functions from module %s" % (module)
    880 	continue;
    881 
    882     # iterate over all functions in the module generating the test
    883     i = 0
    884     nb_tests_old = nb_tests
    885     for function in functions:
    886         i = i + 1
    887         generate_test(module, function);
    888 
    889     # header
    890     test.write("""static int
    891 test_%s(void) {
    892     int test_ret = 0;
    893 
    894     if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n");
    895 """ % (module, module, nb_tests - nb_tests_old, i))
    896 
    897     # iterate over all functions in the module generating the call
    898     for function in functions:
    899         name = function.xpathEval('string(@name)')
    900 	if is_skipped_function(name):
    901 	    continue
    902 	test.write("    test_ret += test_%s();\n" % (name))
    903 
    904     # footer
    905     test.write("""
    906     if (test_ret != 0)
    907 	printf("Module %s: %%d errors\\n", test_ret);
    908     return(test_ret);
    909 }
    910 """ % (module))
    911 
    912 #
    913 # Generate direct module caller
    914 #
    915 test.write("""static int
    916 test_module(const char *module) {
    917 """);
    918 for module in modules:
    919     test.write("""    if (!strcmp(module, "%s")) return(test_%s());\n""" % (
    920         module, module))
    921 test.write("""    return(0);
    922 }
    923 """);
    924 
    925 print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
    926 
    927 compare_and_save()
    928 
    929 missing_list = []
    930 for missing in missing_types.keys():
    931     if missing == 'va_list' or missing == '...':
    932         continue;
    933 
    934     n = len(missing_types[missing])
    935     missing_list.append((n, missing))
    936 
    937 def compare_missing(a, b):
    938     return b[0] - a[0]
    939 
    940 missing_list.sort(compare_missing)
    941 print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))
    942 lst = open("missing.lst", "w")
    943 lst.write("Missing support for %d types" % (len(missing_list)))
    944 lst.write("\n")
    945 for miss in missing_list:
    946     lst.write("%s: %d :" % (miss[1], miss[0]))
    947     i = 0
    948     for n in missing_types[miss[1]]:
    949         i = i + 1
    950         if i > 5:
    951 	    lst.write(" ...")
    952 	    break
    953 	lst.write(" %s" % (n))
    954     lst.write("\n")
    955 lst.write("\n")
    956 lst.write("\n")
    957 lst.write("Missing support per module");
    958 for module in missing_functions.keys():
    959     lst.write("module %s:\n   %s\n" % (module, missing_functions[module]))
    960 
    961 lst.close()
    962 
    963 
    964