gentest.py
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 # 267 # Arguments that should use strlen() instead of gen_int() 268 # 269 use_strlen = { 270 "htmlCreateMemoryParserCtxt": {"size": "buffer"}, 271 "htmlCreatePushParserCtxt": {"size": "chunk"}, 272 "htmlCtxtReadMemory": {"size": "buffer"}, 273 "htmlParseChunk": {"size": "chunk"}, 274 "htmlReadMemory": {"size": "buffer"}, 275 "xmlDictExists": {"len": "name"}, 276 "xmlDictLookup": {"len": "name"}, 277 "xmlCreatePushParserCtxt": {"size": "chunk"}, 278 "xmlCtxtReadMemory": {"size": "buffer"}, 279 "xmlCtxtResetPush": {"size": "chunk"}, 280 "xmlParseChunk": {"size": "chunk"}, 281 "xmlParseInNodeContext": {"datalen": "data"}, 282 "xmlParseMemory": {"size": "buffer"}, 283 "xmlReadMemory": {"size": "buffer"}, 284 "xmlRecoverMemory": {"size": "buffer"}, 285 "xmlSAXParseMemory": {"size": "buffer"}, 286 "xmlSAXParseMemoryWithData": {"size": "buffer"}, 287 "xmlSAXUserParseMemory": {"size": "buffer"}, 288 "xmlCreateMemoryParserCtxt": {"size": "buffer"}, 289 "xmlRelaxNGNewMemParserCtxt": {"size": "buffer"}, 290 "xmlBufferAdd": {"len": "str"}, 291 "xmlBufferAddHead": {"len": "str"}, 292 "xmlBuildQName": {"len": "memory"}, 293 "xmlNewCDataBlock": {"len": "content"}, 294 "xmlNewDocTextLen": {"len": "content"}, 295 "xmlNewTextLen": {"len": "content"}, 296 "xmlNodeAddContentLen": {"len": "content"}, 297 "xmlNodeSetContentLen": {"len": "content"}, 298 "xmlStringLenGetNodeList": {"len": "value"}, 299 "xmlTextConcat": {"len": "content"}, 300 "xmlOutputBufferWrite": {"len": "buf", "-reorder-length": True}, 301 "xmlParserInputBufferCreateMem": {"size": "mem"}, 302 "xmlParserInputBufferCreateStatic": {"size": "mem"}, 303 "xmlParserInputBufferPush": {"len": "buf", "-reorder-length": True}, 304 "xmlReaderForMemory": {"size": "buffer"}, 305 "xmlReaderNewMemory": {"size": "buffer"}, 306 "xmlSchemaNewMemParserCtxt": {"size": "buffer"}, 307 "xmlCharStrndup": {"len": "cur"}, 308 "xmlStrncatNew": {"len": "str2"}, 309 "xmlStrncmp": {"len": "str2"}, 310 "xmlStrndup": {"len": "cur"}, 311 "xmlStrsub": {"len": "str", "-compute-length": "start"}, 312 "xmlTextWriterWriteBase64": {"len": "data", "-compute-length": "start"}, 313 "xmlTextWriterWriteBinHex": {"len": "data", "-compute-length": "start"}, 314 "xmlTextWriterWriteRawLen": {"len": "content"}, 315 } 316 317 modules = [] 318 319 def is_skipped_module(name): 320 for mod in skipped_modules: 321 if mod == name: 322 return 1 323 return 0 324 325 def is_skipped_function(name): 326 for fun in skipped_functions: 327 if fun == name: 328 return 1 329 # Do not test destructors 330 if string.find(name, 'Free') != -1: 331 return 1 332 return 0 333 334 def is_skipped_memcheck(name): 335 for fun in skipped_memcheck: 336 if fun == name: 337 return 1 338 return 0 339 340 missing_types = {} 341 def add_missing_type(name, func): 342 try: 343 list = missing_types[name] 344 list.append(func) 345 except: 346 missing_types[name] = [func] 347 348 generated_param_types = [] 349 def add_generated_param_type(name): 350 generated_param_types.append(name) 351 352 generated_return_types = [] 353 def add_generated_return_type(name): 354 generated_return_types.append(name) 355 356 missing_functions = {} 357 missing_functions_nr = 0 358 def add_missing_functions(name, module): 359 global missing_functions_nr 360 361 missing_functions_nr = missing_functions_nr + 1 362 try: 363 list = missing_functions[module] 364 list.append(name) 365 except: 366 missing_functions[module] = [name] 367 368 # 369 # Provide the type generators and destructors for the parameters 370 # 371 372 def type_convert(str, name, info, module, function, pos): 373 # res = string.replace(str, " ", " ") 374 # res = string.replace(str, " ", " ") 375 # res = string.replace(str, " ", " ") 376 res = string.replace(str, " *", "_ptr") 377 # res = string.replace(str, "*", "_ptr") 378 res = string.replace(res, " ", "_") 379 if res == 'const_char_ptr': 380 if string.find(name, "file") != -1 or \ 381 string.find(name, "uri") != -1 or \ 382 string.find(name, "URI") != -1 or \ 383 string.find(info, "filename") != -1 or \ 384 string.find(info, "URI") != -1 or \ 385 string.find(info, "URL") != -1: 386 if string.find(function, "Save") != -1 or \ 387 string.find(function, "Create") != -1 or \ 388 string.find(function, "Write") != -1 or \ 389 string.find(function, "Fetch") != -1: 390 return('fileoutput') 391 return('filepath') 392 if res == 'void_ptr': 393 if module == 'nanoftp' and name == 'ctx': 394 return('xmlNanoFTPCtxtPtr') 395 if function == 'xmlNanoFTPNewCtxt' or \ 396 function == 'xmlNanoFTPConnectTo' or \ 397 function == 'xmlNanoFTPOpen': 398 return('xmlNanoFTPCtxtPtr') 399 if module == 'nanohttp' and name == 'ctx': 400 return('xmlNanoHTTPCtxtPtr') 401 if function == 'xmlNanoHTTPMethod' or \ 402 function == 'xmlNanoHTTPMethodRedir' or \ 403 function == 'xmlNanoHTTPOpen' or \ 404 function == 'xmlNanoHTTPOpenRedir': 405 return('xmlNanoHTTPCtxtPtr'); 406 if function == 'xmlIOHTTPOpen': 407 return('xmlNanoHTTPCtxtPtr') 408 if string.find(name, "data") != -1: 409 return('userdata') 410 if string.find(name, "user") != -1: 411 return('userdata') 412 if res == 'xmlDoc_ptr': 413 res = 'xmlDocPtr' 414 if res == 'xmlNode_ptr': 415 res = 'xmlNodePtr' 416 if res == 'xmlDict_ptr': 417 res = 'xmlDictPtr' 418 if res == 'xmlNodePtr' and pos != 0: 419 if (function == 'xmlAddChild' and pos == 2) or \ 420 (function == 'xmlAddChildList' and pos == 2) or \ 421 (function == 'xmlAddNextSibling' and pos == 2) or \ 422 (function == 'xmlAddSibling' and pos == 2) or \ 423 (function == 'xmlDocSetRootElement' and pos == 2) or \ 424 (function == 'xmlReplaceNode' and pos == 2) or \ 425 (function == 'xmlTextMerge') or \ 426 (function == 'xmlAddPrevSibling' and pos == 2): 427 return('xmlNodePtr_in'); 428 if res == 'const xmlBufferPtr': 429 res = 'xmlBufferPtr' 430 if res == 'xmlChar_ptr' and name == 'name' and \ 431 string.find(function, "EatName") != -1: 432 return('eaten_name') 433 if res == 'void_ptr*': 434 res = 'void_ptr_ptr' 435 if res == 'char_ptr*': 436 res = 'char_ptr_ptr' 437 if res == 'xmlChar_ptr*': 438 res = 'xmlChar_ptr_ptr' 439 if res == 'const_xmlChar_ptr*': 440 res = 'const_xmlChar_ptr_ptr' 441 if res == 'const_char_ptr*': 442 res = 'const_char_ptr_ptr' 443 if res == 'FILE_ptr' and module == 'debugXML': 444 res = 'debug_FILE_ptr'; 445 if res == 'int' and name == 'options': 446 if module == 'parser' or module == 'xmlreader': 447 res = 'parseroptions' 448 449 return res 450 451 known_param_types = [] 452 453 def is_known_param_type(name, rtype): 454 global test 455 for type in known_param_types: 456 if type == name: 457 return 1 458 for type in generated_param_types: 459 if type == name: 460 return 1 461 462 if name[-3:] == 'Ptr' or name[-4:] == '_ptr': 463 if rtype[0:6] == 'const ': 464 crtype = rtype[6:] 465 else: 466 crtype = rtype 467 468 define = 0 469 if modules_defines.has_key(module): 470 test.write("#ifdef %s\n" % (modules_defines[module])) 471 define = 1 472 test.write(""" 473 #define gen_nb_%s 1 474 static %s gen_%s(int no ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 475 return(NULL); 476 } 477 static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 478 } 479 """ % (name, crtype, name, name, rtype)) 480 if define == 1: 481 test.write("#endif\n\n") 482 add_generated_param_type(name) 483 return 1 484 485 return 0 486 487 # 488 # Provide the type destructors for the return values 489 # 490 491 known_return_types = [] 492 493 def is_known_return_type(name): 494 for type in known_return_types: 495 if type == name: 496 return 1 497 return 0 498 499 # 500 # Copy the beginning of the C test program result 501 # 502 503 try: 504 input = open("testapi.c", "r") 505 except: 506 input = open(srcPref + "testapi.c", "r") 507 test = open('testapi.c.new', 'w') 508 509 def compare_and_save(): 510 global test 511 512 test.close() 513 try: 514 input = open("testapi.c", "r").read() 515 except: 516 input = '' 517 test = open('testapi.c.new', "r").read() 518 if input != test: 519 try: 520 os.system("rm testapi.c; mv testapi.c.new testapi.c") 521 except: 522 os.system("mv testapi.c.new testapi.c") 523 print("Updated testapi.c") 524 else: 525 print("Generated testapi.c is identical") 526 527 line = input.readline() 528 while line != "": 529 if line == "/* CUT HERE: everything below that line is generated */\n": 530 break; 531 if line[0:15] == "#define gen_nb_": 532 type = string.split(line[15:])[0] 533 known_param_types.append(type) 534 if line[0:19] == "static void desret_": 535 type = string.split(line[19:], '(')[0] 536 known_return_types.append(type) 537 test.write(line) 538 line = input.readline() 539 input.close() 540 541 if line == "": 542 print "Could not find the CUT marker in testapi.c skipping generation" 543 test.close() 544 sys.exit(0) 545 546 print("Scanned testapi.c: found %d parameters types and %d return types\n" % ( 547 len(known_param_types), len(known_return_types))) 548 test.write("/* CUT HERE: everything below that line is generated */\n") 549 550 551 # 552 # Open the input API description 553 # 554 doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0) 555 if doc == None: 556 print "Failed to load doc/libxml2-api.xml" 557 sys.exit(1) 558 ctxt = doc.xpathNewContext() 559 560 # 561 # Generate a list of all function parameters and select only 562 # those used in the api tests 563 # 564 argtypes = {} 565 args = ctxt.xpathEval("/api/symbols/function/arg") 566 for arg in args: 567 mod = arg.xpathEval('string(../@file)') 568 func = arg.xpathEval('string(../@name)') 569 if (mod not in skipped_modules) and (func not in skipped_functions): 570 type = arg.xpathEval('string(@type)') 571 if not argtypes.has_key(type): 572 argtypes[type] = func 573 574 # similarly for return types 575 rettypes = {} 576 rets = ctxt.xpathEval("/api/symbols/function/return") 577 for ret in rets: 578 mod = ret.xpathEval('string(../@file)') 579 func = ret.xpathEval('string(../@name)') 580 if (mod not in skipped_modules) and (func not in skipped_functions): 581 type = ret.xpathEval('string(@type)') 582 if not rettypes.has_key(type): 583 rettypes[type] = func 584 585 # 586 # Generate constructors and return type handling for all enums 587 # which are used as function parameters 588 # 589 enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']") 590 for enum in enums: 591 module = enum.xpathEval('string(@file)') 592 name = enum.xpathEval('string(@name)') 593 # 594 # Skip any enums which are not in our filtered lists 595 # 596 if (name == None) or ((name not in argtypes) and (name not in rettypes)): 597 continue; 598 define = 0 599 600 if argtypes.has_key(name) and is_known_param_type(name, name) == 0: 601 values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name) 602 i = 0 603 vals = [] 604 for value in values: 605 vname = value.xpathEval('string(@name)') 606 if vname == None: 607 continue; 608 i = i + 1 609 if i >= 5: 610 break; 611 vals.append(vname) 612 if vals == []: 613 print "Didn't find any value for enum %s" % (name) 614 continue 615 if modules_defines.has_key(module): 616 test.write("#ifdef %s\n" % (modules_defines[module])) 617 define = 1 618 test.write("#define gen_nb_%s %d\n" % (name, len(vals))) 619 test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" % 620 (name, name)) 621 i = 1 622 for value in vals: 623 test.write(" if (no == %d) return(%s);\n" % (i, value)) 624 i = i + 1 625 test.write(""" return(0); 626 } 627 628 static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 629 } 630 631 """ % (name, name)); 632 known_param_types.append(name) 633 634 if (is_known_return_type(name) == 0) and (name in rettypes): 635 if define == 0 and modules_defines.has_key(module): 636 test.write("#ifdef %s\n" % (modules_defines[module])) 637 define = 1 638 test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) { 639 } 640 641 """ % (name, name)) 642 known_return_types.append(name) 643 if define == 1: 644 test.write("#endif\n\n") 645 646 # 647 # Load the interfaces 648 # 649 headers = ctxt.xpathEval("/api/files/file") 650 for file in headers: 651 name = file.xpathEval('string(@name)') 652 if (name == None) or (name == ''): 653 continue 654 655 # 656 # Some module may be skipped because they don't really consists 657 # of user callable APIs 658 # 659 if is_skipped_module(name): 660 continue 661 662 # 663 # do not test deprecated APIs 664 # 665 desc = file.xpathEval('string(description)') 666 if string.find(desc, 'DEPRECATED') != -1: 667 print "Skipping deprecated interface %s" % name 668 continue; 669 670 test.write("#include <libxml/%s.h>\n" % name) 671 modules.append(name) 672 673 # 674 # Generate the callers signatures 675 # 676 for module in modules: 677 test.write("static int test_%s(void);\n" % module); 678 679 # 680 # Generate the top caller 681 # 682 683 test.write(""" 684 /** 685 * testlibxml2: 686 * 687 * Main entry point of the tester for the full libxml2 module, 688 * it calls all the tester entry point for each module. 689 * 690 * Returns the number of error found 691 */ 692 static int 693 testlibxml2(void) 694 { 695 int test_ret = 0; 696 697 """) 698 699 for module in modules: 700 test.write(" test_ret += test_%s();\n" % module) 701 702 test.write(""" 703 printf("Total: %d functions, %d tests, %d errors\\n", 704 function_tests, call_tests, test_ret); 705 return(test_ret); 706 } 707 708 """) 709 710 # 711 # How to handle a function 712 # 713 nb_tests = 0 714 715 def generate_test(module, node): 716 global test 717 global nb_tests 718 nb_cond = 0 719 no_gen = 0 720 721 name = node.xpathEval('string(@name)') 722 if is_skipped_function(name): 723 return 724 725 # 726 # check we know how to handle the args and return values 727 # and store the informations for the generation 728 # 729 try: 730 args = node.xpathEval("arg") 731 except: 732 args = [] 733 t_args = [] 734 n = 0 735 for arg in args: 736 n = n + 1 737 rtype = arg.xpathEval("string(@type)") 738 if rtype == 'void': 739 break; 740 info = arg.xpathEval("string(@info)") 741 nam = arg.xpathEval("string(@name)") 742 type = type_convert(rtype, nam, info, module, name, n) 743 if is_known_param_type(type, rtype) == 0: 744 add_missing_type(type, name); 745 no_gen = 1 746 if (type[-3:] == 'Ptr' or type[-4:] == '_ptr') and \ 747 rtype[0:6] == 'const ': 748 crtype = rtype[6:] 749 else: 750 crtype = rtype 751 t_args.append((nam, type, rtype, crtype, info)) 752 753 try: 754 rets = node.xpathEval("return") 755 except: 756 rets = [] 757 t_ret = None 758 for ret in rets: 759 rtype = ret.xpathEval("string(@type)") 760 info = ret.xpathEval("string(@info)") 761 type = type_convert(rtype, 'return', info, module, name, 0) 762 if rtype == 'void': 763 break 764 if is_known_return_type(type) == 0: 765 add_missing_type(type, name); 766 no_gen = 1 767 t_ret = (type, rtype, info) 768 break 769 770 test.write(""" 771 static int 772 test_%s(void) { 773 int test_ret = 0; 774 775 """ % (name)) 776 777 if no_gen == 1: 778 add_missing_functions(name, module) 779 test.write(""" 780 /* missing type support */ 781 return(test_ret); 782 } 783 784 """) 785 return 786 787 try: 788 conds = node.xpathEval("cond") 789 for cond in conds: 790 test.write("#if %s\n" % (cond.get_content())) 791 nb_cond = nb_cond + 1 792 except: 793 pass 794 795 define = 0 796 if function_defines.has_key(name): 797 test.write("#ifdef %s\n" % (function_defines[name])) 798 define = 1 799 800 # Declare the memory usage counter 801 no_mem = is_skipped_memcheck(name) 802 if no_mem == 0: 803 test.write(" int mem_base;\n"); 804 805 # Declare the return value 806 if t_ret != None: 807 test.write(" %s ret_val;\n" % (t_ret[1])) 808 809 # Declare the arguments 810 for arg in t_args: 811 (nam, type, rtype, crtype, info) = arg; 812 # add declaration 813 test.write(" %s %s; /* %s */\n" % (crtype, nam, info)) 814 if name in use_strlen and nam in use_strlen[name]: 815 continue 816 test.write(" int n_%s;\n" % (nam)) 817 test.write("\n") 818 819 # Cascade loop on of each argument list of values 820 for arg in t_args: 821 (nam, type, rtype, crtype, info) = arg 822 if name in use_strlen and nam in use_strlen[name]: 823 continue 824 # 825 test.write(" for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % ( 826 nam, nam, type, nam)) 827 828 # log the memory usage 829 if no_mem == 0: 830 test.write(" mem_base = xmlMemBlocks();\n"); 831 832 # prepare the call 833 i = 0 834 swap_statement = None 835 for arg in t_args: 836 (nam, type, rtype, crtype, info) = arg 837 # 838 if name in use_strlen and nam in use_strlen[name]: 839 buffer_arg = use_strlen[name][nam] 840 statement = " %s = %s ? strlen((const char *)%s) : 0;\n" % (nam, buffer_arg, buffer_arg) 841 if "-reorder-length" in use_strlen[name]: 842 swap_statement = {buffer_arg: statement} 843 else: 844 test.write(statement) 845 if "-compute-length" in use_strlen[name]: 846 start_arg = use_strlen[name]["-compute-length"] 847 test.write(" %s = ((%s > 0) && (%s > %s)) ? (%s - %s) : 0;\n" % ( 848 nam, nam, nam, start_arg, nam, start_arg)) 849 else: 850 test.write(" %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i)) 851 if swap_statement and nam in swap_statement: 852 test.write(swap_statement[nam]) 853 swap_statement = None 854 i = i + 1 855 856 # do the call, and clanup the result 857 if extra_pre_call.has_key(name): 858 test.write(" %s\n"% (extra_pre_call[name])) 859 if t_ret != None: 860 test.write("\n ret_val = %s(" % (name)) 861 need = 0 862 for arg in t_args: 863 (nam, type, rtype, crtype, info) = arg 864 if need: 865 test.write(", ") 866 else: 867 need = 1 868 if rtype != crtype: 869 test.write("(%s)" % rtype) 870 test.write("%s" % nam); 871 test.write(");\n") 872 if extra_post_call.has_key(name): 873 test.write(" %s\n"% (extra_post_call[name])) 874 test.write(" desret_%s(ret_val);\n" % t_ret[0]) 875 else: 876 test.write("\n %s(" % (name)); 877 need = 0; 878 for arg in t_args: 879 (nam, type, rtype, crtype, info) = arg; 880 if need: 881 test.write(", ") 882 else: 883 need = 1 884 if rtype != crtype: 885 test.write("(%s)" % rtype) 886 test.write("%s" % nam) 887 test.write(");\n") 888 if extra_post_call.has_key(name): 889 test.write(" %s\n"% (extra_post_call[name])) 890 891 test.write(" call_tests++;\n"); 892 893 # Free the arguments 894 i = 0 895 for arg in t_args: 896 (nam, type, rtype, crtype, info) = arg; 897 if name in use_strlen and nam in use_strlen[name]: 898 i = i + 1 899 continue 900 # This is a hack to prevent generating a destructor for the 901 # 'input' argument in xmlTextReaderSetup. There should be 902 # a better, more generic way to do this! 903 if string.find(info, 'destroy') == -1: 904 test.write(" des_%s(n_%s, " % (type, nam)) 905 if rtype != crtype: 906 test.write("(%s)" % rtype) 907 test.write("%s, %d);\n" % (nam, i)) 908 i = i + 1 909 910 test.write(" xmlResetLastError();\n"); 911 # Check the memory usage 912 if no_mem == 0: 913 test.write(""" if (mem_base != xmlMemBlocks()) { 914 printf("Leak of %%d blocks found in %s", 915 xmlMemBlocks() - mem_base); 916 test_ret++; 917 """ % (name)); 918 for arg in t_args: 919 (nam, type, rtype, crtype, info) = arg 920 if name in use_strlen and nam in use_strlen[name]: 921 continue 922 test.write(""" printf(" %%d", n_%s);\n""" % (nam)) 923 test.write(""" printf("\\n");\n""") 924 test.write(" }\n") 925 926 for arg in t_args: 927 if name in use_strlen and arg[0] in use_strlen[name]: 928 continue 929 test.write(" }\n") 930 931 test.write(" function_tests++;\n") 932 # 933 # end of conditional 934 # 935 while nb_cond > 0: 936 test.write("#endif\n") 937 nb_cond = nb_cond -1 938 if define == 1: 939 test.write("#endif\n") 940 941 nb_tests = nb_tests + 1; 942 943 test.write(""" 944 return(test_ret); 945 } 946 947 """) 948 949 # 950 # Generate all module callers 951 # 952 for module in modules: 953 # gather all the functions exported by that module 954 try: 955 functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module)) 956 except: 957 print "Failed to gather functions from module %s" % (module) 958 continue; 959 960 # iterate over all functions in the module generating the test 961 i = 0 962 nb_tests_old = nb_tests 963 for function in functions: 964 i = i + 1 965 generate_test(module, function); 966 967 # header 968 test.write("""static int 969 test_%s(void) { 970 int test_ret = 0; 971 972 if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n"); 973 """ % (module, module, nb_tests - nb_tests_old, i)) 974 975 # iterate over all functions in the module generating the call 976 for function in functions: 977 name = function.xpathEval('string(@name)') 978 if is_skipped_function(name): 979 continue 980 test.write(" test_ret += test_%s();\n" % (name)) 981 982 # footer 983 test.write(""" 984 if (test_ret != 0) 985 printf("Module %s: %%d errors\\n", test_ret); 986 return(test_ret); 987 } 988 """ % (module)) 989 990 # 991 # Generate direct module caller 992 # 993 test.write("""static int 994 test_module(const char *module) { 995 """); 996 for module in modules: 997 test.write(""" if (!strcmp(module, "%s")) return(test_%s());\n""" % ( 998 module, module)) 999 test.write(""" return(0); 1000 } 1001 """); 1002 1003 print "Generated test for %d modules and %d functions" %(len(modules), nb_tests) 1004 1005 compare_and_save() 1006 1007 missing_list = [] 1008 for missing in missing_types.keys(): 1009 if missing == 'va_list' or missing == '...': 1010 continue; 1011 1012 n = len(missing_types[missing]) 1013 missing_list.append((n, missing)) 1014 1015 def compare_missing(a, b): 1016 return b[0] - a[0] 1017 1018 missing_list.sort(compare_missing) 1019 print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list)) 1020 lst = open("missing.lst", "w") 1021 lst.write("Missing support for %d types" % (len(missing_list))) 1022 lst.write("\n") 1023 for miss in missing_list: 1024 lst.write("%s: %d :" % (miss[1], miss[0])) 1025 i = 0 1026 for n in missing_types[miss[1]]: 1027 i = i + 1 1028 if i > 5: 1029 lst.write(" ...") 1030 break 1031 lst.write(" %s" % (n)) 1032 lst.write("\n") 1033 lst.write("\n") 1034 lst.write("\n") 1035 lst.write("Missing support per module"); 1036 for module in missing_functions.keys(): 1037 lst.write("module %s:\n %s\n" % (module, missing_functions[module])) 1038 1039 lst.close() 1040 1041