1#!/usr/bin/env python3 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] 25 26# 27# defines for each module 28# 29modules_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# 52function_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# 100skipped_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# 171skipped_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# 185extra_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} 213extra_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 274modules = [] 275 276def is_skipped_module(name): 277 for mod in skipped_modules: 278 if mod == name: 279 return 1 280 return 0 281 282def 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 291def is_skipped_memcheck(name): 292 for fun in skipped_memcheck: 293 if fun == name: 294 return 1 295 return 0 296 297missing_types = {} 298def add_missing_type(name, func): 299 try: 300 list = missing_types[name] 301 list.append(func) 302 except: 303 missing_types[name] = [func] 304 305generated_param_types = [] 306def add_generated_param_type(name): 307 generated_param_types.append(name) 308 309generated_return_types = [] 310def add_generated_return_type(name): 311 generated_return_types.append(name) 312 313missing_functions = {} 314missing_functions_nr = 0 315def 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 329def 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 408known_param_types = [] 409 410def 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 416def 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 define = 0 427 if module in modules_defines: 428 test.write("#ifdef %s\n" % (modules_defines[module])) 429 define = 1 430 test.write(""" 431#define gen_nb_%s 1 432#define gen_%s(no, nr) NULL 433#define des_%s(no, val, nr) 434""" % (name, name, name)) 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 = line[15:].split()[0] 485 known_param_types.append(type) 486 if line[0:19] == "static void desret_": 487 type = line[19:].split('(')[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 type not in argtypes: 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 type not in rettypes: 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 (name in argtypes) 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 module in modules_defines: 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 (module in modules_defines): 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 desc.find('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 t_args.append((nam, type, rtype, info)) 699 700 try: 701 rets = node.xpathEval("return") 702 except: 703 rets = [] 704 t_ret = None 705 for ret in rets: 706 rtype = ret.xpathEval("string(@type)") 707 info = ret.xpathEval("string(@info)") 708 type = type_convert(rtype, 'return', info, module, name, 0) 709 if rtype == 'void': 710 break 711 if is_known_return_type(type) == 0: 712 add_missing_type(type, name); 713 no_gen = 1 714 t_ret = (type, rtype, info) 715 break 716 717 if no_gen == 0: 718 for t_arg in t_args: 719 (nam, type, rtype, info) = t_arg 720 generate_param_type(type, rtype) 721 722 test.write(""" 723static int 724test_%s(void) { 725 int test_ret = 0; 726 727""" % (name)) 728 729 if no_gen == 1: 730 add_missing_functions(name, module) 731 test.write(""" 732 /* missing type support */ 733 return(test_ret); 734} 735 736""") 737 return 738 739 try: 740 conds = node.xpathEval("cond") 741 for cond in conds: 742 test.write("#if %s\n" % (cond.get_content())) 743 nb_cond = nb_cond + 1 744 except: 745 pass 746 747 define = 0 748 if name in function_defines: 749 test.write("#ifdef %s\n" % (function_defines[name])) 750 define = 1 751 752 # Declare the memory usage counter 753 no_mem = is_skipped_memcheck(name) 754 if no_mem == 0: 755 test.write(" int mem_base;\n"); 756 757 # Declare the return value 758 if t_ret != None: 759 test.write(" %s ret_val;\n" % (t_ret[1])) 760 761 # Declare the arguments 762 for arg in t_args: 763 (nam, type, rtype, info) = arg; 764 # add declaration 765 test.write(" %s %s; /* %s */\n" % (rtype, nam, info)) 766 test.write(" int n_%s;\n" % (nam)) 767 test.write("\n") 768 769 # Cascade loop on of each argument list of values 770 for arg in t_args: 771 (nam, type, rtype, info) = arg; 772 # 773 test.write(" for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % ( 774 nam, nam, type, nam)) 775 776 # log the memory usage 777 if no_mem == 0: 778 test.write(" mem_base = xmlMemBlocks();\n"); 779 780 # prepare the call 781 i = 0; 782 for arg in t_args: 783 (nam, type, rtype, info) = arg; 784 # 785 test.write(" %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i)) 786 i = i + 1; 787 788 # add checks to avoid out-of-bounds array access 789 i = 0; 790 for arg in t_args: 791 (nam, type, rtype, info) = arg; 792 # assume that "size", "len", and "start" parameters apply to either 793 # the nearest preceding or following char pointer 794 if type == "int" and (nam == "size" or nam == "len" or nam == "start"): 795 for j in (list(range(i - 1, -1, -1)) + list(range(i + 1, len(t_args)))): 796 (bnam, btype) = t_args[j][:2] 797 if btype == "const_char_ptr" or btype == "const_xmlChar_ptr": 798 test.write( 799 " if ((%s != NULL) &&\n" 800 " (%s > xmlStrlen(BAD_CAST %s)))\n" 801 " %s = 0;\n" 802 % (bnam, nam, bnam, nam)) 803 break 804 i = i + 1; 805 806 # do the call, and clanup the result 807 if name in extra_pre_call: 808 test.write(" %s\n"% (extra_pre_call[name])) 809 if t_ret != None: 810 test.write("\n ret_val = %s(" % (name)) 811 need = 0 812 for arg in t_args: 813 (nam, type, rtype, info) = arg 814 if need: 815 test.write(", ") 816 else: 817 need = 1 818 test.write("%s" % nam); 819 test.write(");\n") 820 if name in extra_post_call: 821 test.write(" %s\n"% (extra_post_call[name])) 822 test.write(" desret_%s(ret_val);\n" % t_ret[0]) 823 else: 824 test.write("\n %s(" % (name)); 825 need = 0; 826 for arg in t_args: 827 (nam, type, rtype, info) = arg; 828 if need: 829 test.write(", ") 830 else: 831 need = 1 832 test.write("%s" % nam) 833 test.write(");\n") 834 if name in extra_post_call: 835 test.write(" %s\n"% (extra_post_call[name])) 836 837 test.write(" call_tests++;\n"); 838 839 # Free the arguments 840 i = 0; 841 for arg in t_args: 842 (nam, type, rtype, info) = arg; 843 # This is a hack to prevent generating a destructor for the 844 # 'input' argument in xmlTextReaderSetup. There should be 845 # a better, more generic way to do this! 846 if info.find('destroy') == -1: 847 test.write(" des_%s(n_%s, " % (type, nam)) 848 test.write("%s, %d);\n" % (nam, i)) 849 i = i + 1; 850 851 test.write(" xmlResetLastError();\n"); 852 # Check the memory usage 853 if no_mem == 0: 854 test.write(""" if (mem_base != xmlMemBlocks()) { 855 printf("Leak of %%d blocks found in %s", 856\t xmlMemBlocks() - mem_base); 857\t test_ret++; 858""" % (name)); 859 for arg in t_args: 860 (nam, type, rtype, info) = arg; 861 test.write(""" printf(" %%d", n_%s);\n""" % (nam)) 862 test.write(""" printf("\\n");\n""") 863 test.write(" }\n") 864 865 for arg in t_args: 866 test.write(" }\n") 867 868 test.write(" function_tests++;\n") 869 # 870 # end of conditional 871 # 872 while nb_cond > 0: 873 test.write("#endif\n") 874 nb_cond = nb_cond -1 875 if define == 1: 876 test.write("#endif\n") 877 878 nb_tests = nb_tests + 1; 879 880 test.write(""" 881 return(test_ret); 882} 883 884""") 885 886# 887# Generate all module callers 888# 889for module in modules: 890 # gather all the functions exported by that module 891 try: 892 functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module)) 893 except: 894 print("Failed to gather functions from module %s" % (module)) 895 continue; 896 897 # iterate over all functions in the module generating the test 898 i = 0 899 nb_tests_old = nb_tests 900 for function in functions: 901 i = i + 1 902 generate_test(module, function); 903 904 # header 905 test.write("""static int 906test_%s(void) { 907 int test_ret = 0; 908 909 if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n"); 910""" % (module, module, nb_tests - nb_tests_old, i)) 911 912 # iterate over all functions in the module generating the call 913 for function in functions: 914 name = function.xpathEval('string(@name)') 915 if is_skipped_function(name): 916 continue 917 test.write(" test_ret += test_%s();\n" % (name)) 918 919 # footer 920 test.write(""" 921 if (test_ret != 0) 922\tprintf("Module %s: %%d errors\\n", test_ret); 923 return(test_ret); 924} 925""" % (module)) 926 927# 928# Generate direct module caller 929# 930test.write("""static int 931test_module(const char *module) { 932"""); 933for module in modules: 934 test.write(""" if (!strcmp(module, "%s")) return(test_%s());\n""" % ( 935 module, module)) 936test.write(""" return(0); 937} 938"""); 939 940print("Generated test for %d modules and %d functions" %(len(modules), nb_tests)) 941 942compare_and_save() 943 944missing_list = [] 945for missing in missing_types.keys(): 946 if missing == 'va_list' or missing == '...': 947 continue; 948 949 n = len(missing_types[missing]) 950 missing_list.append((n, missing)) 951 952missing_list.sort(key=lambda a: a[0]) 953print("Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))) 954lst = open("missing.lst", "w") 955lst.write("Missing support for %d types" % (len(missing_list))) 956lst.write("\n") 957for miss in missing_list: 958 lst.write("%s: %d :" % (miss[1], miss[0])) 959 i = 0 960 for n in missing_types[miss[1]]: 961 i = i + 1 962 if i > 5: 963 lst.write(" ...") 964 break 965 lst.write(" %s" % (n)) 966 lst.write("\n") 967lst.write("\n") 968lst.write("\n") 969lst.write("Missing support per module"); 970for module in missing_functions.keys(): 971 lst.write("module %s:\n %s\n" % (module, missing_functions[module])) 972 973lst.close() 974 975 976