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