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