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