1#!/usr/bin/python -u 2# 3# generate a tester program for the API 4# 5import sys 6import os 7import string 8try: 9 import libxml2 10except: 11 print "libxml2 python bindings not available, skipping testapi.c generation" 12 sys.exit(0) 13 14if len(sys.argv) > 1: 15 srcPref = sys.argv[1] + '/' 16else: 17 srcPref = '' 18 19# 20# Modules we want to skip in API test 21# 22skipped_modules = [ "SAX", "xlink", "threads", "globals", 23 "xmlmemory", "xmlversion", "xmlexports", 24 #deprecated 25 "DOCBparser", 26] 27 28# 29# defines for each module 30# 31modules_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# 55function_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# 109skipped_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 information 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# 170skipped_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# 184extra_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} 208extra_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 266modules = [] 267 268def is_skipped_module(name): 269 for mod in skipped_modules: 270 if mod == name: 271 return 1 272 return 0 273 274def 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 283def is_skipped_memcheck(name): 284 for fun in skipped_memcheck: 285 if fun == name: 286 return 1 287 return 0 288 289missing_types = {} 290def add_missing_type(name, func): 291 try: 292 list = missing_types[name] 293 list.append(func) 294 except: 295 missing_types[name] = [func] 296 297generated_param_types = [] 298def add_generated_param_type(name): 299 generated_param_types.append(name) 300 301generated_return_types = [] 302def add_generated_return_type(name): 303 generated_return_types.append(name) 304 305missing_functions = {} 306missing_functions_nr = 0 307def 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 321def 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 400known_param_types = [] 401 402def is_known_param_type(name): 403 for type in known_param_types: 404 if type == name: 405 return 1 406 return name[-3:] == 'Ptr' or name[-4:] == '_ptr' 407 408def generate_param_type(name, rtype): 409 global test 410 for type in known_param_types: 411 if type == name: 412 return 413 for type in generated_param_types: 414 if type == name: 415 return 416 417 if name[-3:] == 'Ptr' or name[-4:] == '_ptr': 418 if rtype[0:6] == 'const ': 419 crtype = rtype[6:] 420 else: 421 crtype = rtype 422 423 define = 0 424 if modules_defines.has_key(module): 425 test.write("#ifdef %s\n" % (modules_defines[module])) 426 define = 1 427 test.write(""" 428#define gen_nb_%s 1 429static %s gen_%s(int no ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 430 return(NULL); 431} 432static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 433} 434""" % (name, crtype, name, name, rtype)) 435 if define == 1: 436 test.write("#endif\n\n") 437 add_generated_param_type(name) 438 439# 440# Provide the type destructors for the return values 441# 442 443known_return_types = [] 444 445def is_known_return_type(name): 446 for type in known_return_types: 447 if type == name: 448 return 1 449 return 0 450 451# 452# Copy the beginning of the C test program result 453# 454 455try: 456 input = open("testapi.c", "r") 457except: 458 input = open(srcPref + "testapi.c", "r") 459test = open('testapi.c.new', 'w') 460 461def compare_and_save(): 462 global test 463 464 test.close() 465 try: 466 input = open("testapi.c", "r").read() 467 except: 468 input = '' 469 test = open('testapi.c.new', "r").read() 470 if input != test: 471 try: 472 os.system("rm testapi.c; mv testapi.c.new testapi.c") 473 except: 474 os.system("mv testapi.c.new testapi.c") 475 print("Updated testapi.c") 476 else: 477 print("Generated testapi.c is identical") 478 479line = input.readline() 480while line != "": 481 if line == "/* CUT HERE: everything below that line is generated */\n": 482 break; 483 if line[0:15] == "#define gen_nb_": 484 type = string.split(line[15:])[0] 485 known_param_types.append(type) 486 if line[0:19] == "static void desret_": 487 type = string.split(line[19:], '(')[0] 488 known_return_types.append(type) 489 test.write(line) 490 line = input.readline() 491input.close() 492 493if line == "": 494 print "Could not find the CUT marker in testapi.c skipping generation" 495 test.close() 496 sys.exit(0) 497 498print("Scanned testapi.c: found %d parameters types and %d return types\n" % ( 499 len(known_param_types), len(known_return_types))) 500test.write("/* CUT HERE: everything below that line is generated */\n") 501 502 503# 504# Open the input API description 505# 506doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0) 507if doc == None: 508 print "Failed to load doc/libxml2-api.xml" 509 sys.exit(1) 510ctxt = doc.xpathNewContext() 511 512# 513# Generate a list of all function parameters and select only 514# those used in the api tests 515# 516argtypes = {} 517args = ctxt.xpathEval("/api/symbols/function/arg") 518for arg in args: 519 mod = arg.xpathEval('string(../@file)') 520 func = arg.xpathEval('string(../@name)') 521 if (mod not in skipped_modules) and (func not in skipped_functions): 522 type = arg.xpathEval('string(@type)') 523 if not argtypes.has_key(type): 524 argtypes[type] = func 525 526# similarly for return types 527rettypes = {} 528rets = ctxt.xpathEval("/api/symbols/function/return") 529for ret in rets: 530 mod = ret.xpathEval('string(../@file)') 531 func = ret.xpathEval('string(../@name)') 532 if (mod not in skipped_modules) and (func not in skipped_functions): 533 type = ret.xpathEval('string(@type)') 534 if not rettypes.has_key(type): 535 rettypes[type] = func 536 537# 538# Generate constructors and return type handling for all enums 539# which are used as function parameters 540# 541enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']") 542for enum in enums: 543 module = enum.xpathEval('string(@file)') 544 name = enum.xpathEval('string(@name)') 545 # 546 # Skip any enums which are not in our filtered lists 547 # 548 if (name == None) or ((name not in argtypes) and (name not in rettypes)): 549 continue; 550 define = 0 551 552 if argtypes.has_key(name) and is_known_param_type(name) == 0: 553 values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name) 554 i = 0 555 vals = [] 556 for value in values: 557 vname = value.xpathEval('string(@name)') 558 if vname == None: 559 continue; 560 i = i + 1 561 if i >= 5: 562 break; 563 vals.append(vname) 564 if vals == []: 565 print "Didn't find any value for enum %s" % (name) 566 continue 567 if modules_defines.has_key(module): 568 test.write("#ifdef %s\n" % (modules_defines[module])) 569 define = 1 570 test.write("#define gen_nb_%s %d\n" % (name, len(vals))) 571 test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" % 572 (name, name)) 573 i = 1 574 for value in vals: 575 test.write(" if (no == %d) return(%s);\n" % (i, value)) 576 i = i + 1 577 test.write(""" return(0); 578} 579 580static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 581} 582 583""" % (name, name)); 584 known_param_types.append(name) 585 586 if (is_known_return_type(name) == 0) and (name in rettypes): 587 if define == 0 and modules_defines.has_key(module): 588 test.write("#ifdef %s\n" % (modules_defines[module])) 589 define = 1 590 test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) { 591} 592 593""" % (name, name)) 594 known_return_types.append(name) 595 if define == 1: 596 test.write("#endif\n\n") 597 598# 599# Load the interfaces 600# 601headers = ctxt.xpathEval("/api/files/file") 602for file in headers: 603 name = file.xpathEval('string(@name)') 604 if (name == None) or (name == ''): 605 continue 606 607 # 608 # Some module may be skipped because they don't really consists 609 # of user callable APIs 610 # 611 if is_skipped_module(name): 612 continue 613 614 # 615 # do not test deprecated APIs 616 # 617 desc = file.xpathEval('string(description)') 618 if string.find(desc, 'DEPRECATED') != -1: 619 print "Skipping deprecated interface %s" % name 620 continue; 621 622 test.write("#include <libxml/%s.h>\n" % name) 623 modules.append(name) 624 625# 626# Generate the callers signatures 627# 628for module in modules: 629 test.write("static int test_%s(void);\n" % module); 630 631# 632# Generate the top caller 633# 634 635test.write(""" 636/** 637 * testlibxml2: 638 * 639 * Main entry point of the tester for the full libxml2 module, 640 * it calls all the tester entry point for each module. 641 * 642 * Returns the number of error found 643 */ 644static int 645testlibxml2(void) 646{ 647 int test_ret = 0; 648 649""") 650 651for module in modules: 652 test.write(" test_ret += test_%s();\n" % module) 653 654test.write(""" 655 printf("Total: %d functions, %d tests, %d errors\\n", 656 function_tests, call_tests, test_ret); 657 return(test_ret); 658} 659 660""") 661 662# 663# How to handle a function 664# 665nb_tests = 0 666 667def generate_test(module, node): 668 global test 669 global nb_tests 670 nb_cond = 0 671 no_gen = 0 672 673 name = node.xpathEval('string(@name)') 674 if is_skipped_function(name): 675 return 676 677 # 678 # check we know how to handle the args and return values 679 # and store the information for the generation 680 # 681 try: 682 args = node.xpathEval("arg") 683 except: 684 args = [] 685 t_args = [] 686 n = 0 687 for arg in args: 688 n = n + 1 689 rtype = arg.xpathEval("string(@type)") 690 if rtype == 'void': 691 break; 692 info = arg.xpathEval("string(@info)") 693 nam = arg.xpathEval("string(@name)") 694 type = type_convert(rtype, nam, info, module, name, n) 695 if is_known_param_type(type) == 0: 696 add_missing_type(type, name); 697 no_gen = 1 698 if (type[-3:] == 'Ptr' or type[-4:] == '_ptr') and \ 699 rtype[0:6] == 'const ': 700 crtype = rtype[6:] 701 else: 702 crtype = rtype 703 t_args.append((nam, type, rtype, crtype, info)) 704 705 try: 706 rets = node.xpathEval("return") 707 except: 708 rets = [] 709 t_ret = None 710 for ret in rets: 711 rtype = ret.xpathEval("string(@type)") 712 info = ret.xpathEval("string(@info)") 713 type = type_convert(rtype, 'return', info, module, name, 0) 714 if rtype == 'void': 715 break 716 if is_known_return_type(type) == 0: 717 add_missing_type(type, name); 718 no_gen = 1 719 t_ret = (type, rtype, info) 720 break 721 722 if no_gen == 0: 723 for t_arg in t_args: 724 (nam, type, rtype, crtype, info) = t_arg 725 generate_param_type(type, rtype) 726 727 test.write(""" 728static int 729test_%s(void) { 730 int test_ret = 0; 731 732""" % (name)) 733 734 if no_gen == 1: 735 add_missing_functions(name, module) 736 test.write(""" 737 /* missing type support */ 738 return(test_ret); 739} 740 741""") 742 return 743 744 try: 745 conds = node.xpathEval("cond") 746 for cond in conds: 747 test.write("#if %s\n" % (cond.get_content())) 748 nb_cond = nb_cond + 1 749 except: 750 pass 751 752 define = 0 753 if function_defines.has_key(name): 754 test.write("#ifdef %s\n" % (function_defines[name])) 755 define = 1 756 757 # Declare the memory usage counter 758 no_mem = is_skipped_memcheck(name) 759 if no_mem == 0: 760 test.write(" int mem_base;\n"); 761 762 # Declare the return value 763 if t_ret != None: 764 test.write(" %s ret_val;\n" % (t_ret[1])) 765 766 # Declare the arguments 767 for arg in t_args: 768 (nam, type, rtype, crtype, info) = arg; 769 # add declaration 770 test.write(" %s %s; /* %s */\n" % (crtype, nam, info)) 771 test.write(" int n_%s;\n" % (nam)) 772 test.write("\n") 773 774 # Cascade loop on of each argument list of values 775 for arg in t_args: 776 (nam, type, rtype, crtype, info) = arg; 777 # 778 test.write(" for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % ( 779 nam, nam, type, nam)) 780 781 # log the memory usage 782 if no_mem == 0: 783 test.write(" mem_base = xmlMemBlocks();\n"); 784 785 # prepare the call 786 i = 0; 787 for arg in t_args: 788 (nam, type, rtype, crtype, info) = arg; 789 # 790 test.write(" %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i)) 791 i = i + 1; 792 793 # add checks to avoid out-of-bounds array access 794 i = 0; 795 for arg in t_args: 796 (nam, type, rtype, crtype, info) = arg; 797 # assume that "size", "len", and "start" parameters apply to either 798 # the nearest preceding or following char pointer 799 if type == "int" and (nam == "size" or nam == "len" or nam == "start"): 800 for j in range(i - 1, -1, -1) + range(i + 1, len(t_args)): 801 (bnam, btype) = t_args[j][:2] 802 if btype == "const_char_ptr" or btype == "const_xmlChar_ptr": 803 test.write( 804 " if ((%s != NULL) &&\n" 805 " (%s > (int) strlen((const char *) %s) + 1))\n" 806 " continue;\n" 807 % (bnam, nam, bnam)) 808 break 809 i = i + 1; 810 811 # do the call, and clanup the result 812 if extra_pre_call.has_key(name): 813 test.write(" %s\n"% (extra_pre_call[name])) 814 if t_ret != None: 815 test.write("\n ret_val = %s(" % (name)) 816 need = 0 817 for arg in t_args: 818 (nam, type, rtype, crtype, info) = arg 819 if need: 820 test.write(", ") 821 else: 822 need = 1 823 if rtype != crtype: 824 test.write("(%s)" % rtype) 825 test.write("%s" % nam); 826 test.write(");\n") 827 if extra_post_call.has_key(name): 828 test.write(" %s\n"% (extra_post_call[name])) 829 test.write(" desret_%s(ret_val);\n" % t_ret[0]) 830 else: 831 test.write("\n %s(" % (name)); 832 need = 0; 833 for arg in t_args: 834 (nam, type, rtype, crtype, info) = arg; 835 if need: 836 test.write(", ") 837 else: 838 need = 1 839 if rtype != crtype: 840 test.write("(%s)" % rtype) 841 test.write("%s" % nam) 842 test.write(");\n") 843 if extra_post_call.has_key(name): 844 test.write(" %s\n"% (extra_post_call[name])) 845 846 test.write(" call_tests++;\n"); 847 848 # Free the arguments 849 i = 0; 850 for arg in t_args: 851 (nam, type, rtype, crtype, info) = arg; 852 # This is a hack to prevent generating a destructor for the 853 # 'input' argument in xmlTextReaderSetup. There should be 854 # a better, more generic way to do this! 855 if string.find(info, 'destroy') == -1: 856 test.write(" des_%s(n_%s, " % (type, nam)) 857 if rtype != crtype: 858 test.write("(%s)" % rtype) 859 test.write("%s, %d);\n" % (nam, i)) 860 i = i + 1; 861 862 test.write(" xmlResetLastError();\n"); 863 # Check the memory usage 864 if no_mem == 0: 865 test.write(""" if (mem_base != xmlMemBlocks()) { 866 printf("Leak of %%d blocks found in %s", 867 xmlMemBlocks() - mem_base); 868 test_ret++; 869""" % (name)); 870 for arg in t_args: 871 (nam, type, rtype, crtype, info) = arg; 872 test.write(""" printf(" %%d", n_%s);\n""" % (nam)) 873 test.write(""" printf("\\n");\n""") 874 test.write(" }\n") 875 876 for arg in t_args: 877 test.write(" }\n") 878 879 test.write(" function_tests++;\n") 880 # 881 # end of conditional 882 # 883 while nb_cond > 0: 884 test.write("#endif\n") 885 nb_cond = nb_cond -1 886 if define == 1: 887 test.write("#endif\n") 888 889 nb_tests = nb_tests + 1; 890 891 test.write(""" 892 return(test_ret); 893} 894 895""") 896 897# 898# Generate all module callers 899# 900for module in modules: 901 # gather all the functions exported by that module 902 try: 903 functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module)) 904 except: 905 print "Failed to gather functions from module %s" % (module) 906 continue; 907 908 # iterate over all functions in the module generating the test 909 i = 0 910 nb_tests_old = nb_tests 911 for function in functions: 912 i = i + 1 913 generate_test(module, function); 914 915 # header 916 test.write("""static int 917test_%s(void) { 918 int test_ret = 0; 919 920 if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n"); 921""" % (module, module, nb_tests - nb_tests_old, i)) 922 923 # iterate over all functions in the module generating the call 924 for function in functions: 925 name = function.xpathEval('string(@name)') 926 if is_skipped_function(name): 927 continue 928 test.write(" test_ret += test_%s();\n" % (name)) 929 930 # footer 931 test.write(""" 932 if (test_ret != 0) 933 printf("Module %s: %%d errors\\n", test_ret); 934 return(test_ret); 935} 936""" % (module)) 937 938# 939# Generate direct module caller 940# 941test.write("""static int 942test_module(const char *module) { 943"""); 944for module in modules: 945 test.write(""" if (!strcmp(module, "%s")) return(test_%s());\n""" % ( 946 module, module)) 947test.write(""" return(0); 948} 949"""); 950 951print "Generated test for %d modules and %d functions" %(len(modules), nb_tests) 952 953compare_and_save() 954 955missing_list = [] 956for missing in missing_types.keys(): 957 if missing == 'va_list' or missing == '...': 958 continue; 959 960 n = len(missing_types[missing]) 961 missing_list.append((n, missing)) 962 963def compare_missing(a, b): 964 return b[0] - a[0] 965 966missing_list.sort(compare_missing) 967print "Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list)) 968lst = open("missing.lst", "w") 969lst.write("Missing support for %d types" % (len(missing_list))) 970lst.write("\n") 971for miss in missing_list: 972 lst.write("%s: %d :" % (miss[1], miss[0])) 973 i = 0 974 for n in missing_types[miss[1]]: 975 i = i + 1 976 if i > 5: 977 lst.write(" ...") 978 break 979 lst.write(" %s" % (n)) 980 lst.write("\n") 981lst.write("\n") 982lst.write("\n") 983lst.write("Missing support per module"); 984for module in missing_functions.keys(): 985 lst.write("module %s:\n %s\n" % (module, missing_functions[module])) 986 987lst.close() 988 989 990