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", "nanoftp", 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", 108"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", "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] 159 160# 161# These functions have side effects on the global state 162# and hence generate errors on memory allocation tests 163# 164skipped_memcheck = [ "xmlLoadCatalog", "xmlAddEncodingAlias", 165 "xmlSchemaInitTypes", 166 "xmlNanoHTTPScanProxy", "xmlResetLastError", "xmlCatalogConvert", 167 "xmlCatalogRemove", "xmlLoadCatalogs", "xmlCleanupCharEncodingHandlers", 168 "xmlInitCharEncodingHandlers", "xmlCatalogCleanup", 169 "xmlSchemaGetBuiltInType", 170 "htmlParseFile", "htmlCtxtReadFile", # loads the catalogs 171 "xmlTextReaderSchemaValidate", "xmlSchemaCleanupTypes", # initialize the schemas type system 172 "xmlCatalogResolve", "xmlIOParseDTD" # loads the catalogs 173] 174 175# 176# Extra code needed for some test cases 177# 178extra_pre_call = { 179 "xmlSAXUserParseFile": """ 180#ifdef LIBXML_SAX1_ENABLED 181 if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL; 182#endif 183""", 184 "xmlSAXUserParseMemory": """ 185#ifdef LIBXML_SAX1_ENABLED 186 if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL; 187#endif 188""", 189 "xmlParseBalancedChunkMemory": """ 190#ifdef LIBXML_SAX1_ENABLED 191 if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL; 192#endif 193""", 194 "xmlParseBalancedChunkMemoryRecover": """ 195#ifdef LIBXML_SAX1_ENABLED 196 if (sax == (xmlSAXHandlerPtr)&xmlDefaultSAXHandler) user_data = NULL; 197#endif 198""", 199 "xmlParserInputBufferCreateFd": 200 "if (fd >= 0) fd = -1;", 201 "xmlSAXDefaultVersion": """ 202 { 203 int original_version = xmlSAXDefaultVersion(2); 204""", 205} 206extra_post_call = { 207 "xmlAddChild": 208 "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }", 209 "xmlAddChildList": 210 "if (ret_val == NULL) { xmlFreeNodeList(cur) ; cur = NULL ; }", 211 "xmlAddSibling": 212 "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }", 213 "xmlAddNextSibling": 214 "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }", 215 "xmlAddPrevSibling": 216 "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }", 217 "xmlDocSetRootElement": 218 "if (doc == NULL) { xmlFreeNode(root) ; root = NULL ; }", 219 "xmlReplaceNode": 220 """if (cur != NULL) { 221 xmlUnlinkNode(cur); 222 xmlFreeNode(cur) ; cur = NULL ; } 223 if (old != NULL) { 224 xmlUnlinkNode(old); 225 xmlFreeNode(old) ; old = NULL ; } 226\t ret_val = NULL;""", 227 "xmlTextMerge": 228 """if (ret_val == NULL) { 229 xmlUnlinkNode(second); 230 xmlFreeNode(second) ; second = NULL ; 231 ret_val = first; }""", 232 "xmlBuildQName": 233 """if ((ret_val != NULL) && (ret_val != ncname) && 234 (ret_val != prefix) && (ret_val != memory)) 235 xmlFree(ret_val); 236\t ret_val = NULL;""", 237 "xmlNewDocElementContent": 238 """xmlFreeDocElementContent(doc, ret_val); ret_val = NULL;""", 239 "xmlDictReference": "xmlDictFree(dict);", 240 # Functions which deallocates one of their parameters 241 "xmlXPathConvertBoolean": """val = NULL;""", 242 "xmlXPathConvertNumber": """val = NULL;""", 243 "xmlXPathConvertString": """val = NULL;""", 244 "xmlSaveFileTo": """buf = NULL;""", 245 "xmlSaveFormatFileTo": """buf = NULL;""", 246 "xmlIOParseDTD": "input = NULL;", 247 "xmlRemoveProp": "cur = NULL;", 248 "xmlNewNs": "if ((node == NULL) && (ret_val != NULL)) xmlFreeNs(ret_val);", 249 "xmlCopyNamespace": "if (ret_val != NULL) xmlFreeNs(ret_val);", 250 "xmlCopyNamespaceList": "if (ret_val != NULL) xmlFreeNsList(ret_val);", 251 "xmlNewTextWriter": "if (ret_val != NULL) out = NULL;", 252 "xmlNewTextWriterPushParser": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;} if (ret_val != NULL) ctxt = NULL;", 253 "xmlNewIOInputStream": "if (ret_val != NULL) buf = NULL;", 254 "htmlParseChunk": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}", 255 "htmlParseDocument": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}", 256 "xmlParseDocument": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}", 257 "xmlParseChunk": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}", 258 "xmlParseExtParsedEnt": "if (ctxt != NULL) {xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL;}", 259 "xmlDOMWrapAdoptNode": "if ((node != NULL) && (node->parent == NULL)) {xmlUnlinkNode(node);xmlFreeNode(node);node = NULL;}", 260 "xmlSAXDefaultVersion": """ 261 (void)xmlSAXDefaultVersion(original_version); 262 } 263""", 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 name.find('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 = str.replace(" ", " ") 323# res = str.replace(" ", " ") 324# res = str.replace(" ", " ") 325 res = str.replace(" *", "_ptr") 326# res = str.replace("*", "_ptr") 327 res = res.replace(" ", "_") 328 if res == 'const_char_ptr': 329 if name.find("file") != -1 or \ 330 name.find("uri") != -1 or \ 331 name.find("URI") != -1 or \ 332 info.find("filename") != -1 or \ 333 info.find("URI") != -1 or \ 334 info.find("URL") != -1: 335 if function.find("Save") != -1 or \ 336 function.find("Create") != -1 or \ 337 function.find("Write") != -1 or \ 338 function.find("Fetch") != -1: 339 return('fileoutput') 340 return('filepath') 341 if res == 'void_ptr': 342 if module == 'nanohttp' and name == 'ctx': 343 return('xmlNanoHTTPCtxtPtr') 344 if function == 'xmlNanoHTTPMethod' or \ 345 function == 'xmlNanoHTTPMethodRedir' or \ 346 function == 'xmlNanoHTTPOpen' or \ 347 function == 'xmlNanoHTTPOpenRedir': 348 return('xmlNanoHTTPCtxtPtr'); 349 if function == 'xmlIOHTTPOpen': 350 return('xmlNanoHTTPCtxtPtr') 351 if name.find("data") != -1: 352 return('userdata') 353 if name.find("user") != -1: 354 return('userdata') 355 if res == 'xmlDoc_ptr': 356 res = 'xmlDocPtr' 357 if res == 'xmlNode_ptr': 358 res = 'xmlNodePtr' 359 if res == 'xmlDict_ptr': 360 res = 'xmlDictPtr' 361 if res == 'xmlNodePtr' and pos != 0: 362 if (function == 'xmlAddChild' and pos == 2) or \ 363 (function == 'xmlAddChildList' and pos == 2) or \ 364 (function == 'xmlAddNextSibling' and pos == 2) or \ 365 (function == 'xmlAddSibling' and pos == 2) or \ 366 (function == 'xmlDocSetRootElement' and pos == 2) or \ 367 (function == 'xmlReplaceNode' and pos == 2) or \ 368 (function == 'xmlTextMerge') or \ 369 (function == 'xmlAddPrevSibling' and pos == 2): 370 return('xmlNodePtr_in'); 371 if res == 'const xmlBufferPtr': 372 res = 'xmlBufferPtr' 373 if res == 'xmlChar_ptr' and name == 'name' and \ 374 function.find("EatName") != -1: 375 return('eaten_name') 376 if res == 'void_ptr*': 377 res = 'void_ptr_ptr' 378 if res == 'char_ptr*': 379 res = 'char_ptr_ptr' 380 if res == 'xmlChar_ptr*': 381 res = 'xmlChar_ptr_ptr' 382 if res == 'const_xmlChar_ptr*': 383 res = 'const_xmlChar_ptr_ptr' 384 if res == 'const_char_ptr*': 385 res = 'const_char_ptr_ptr' 386 if res == 'FILE_ptr' and module == 'debugXML': 387 res = 'debug_FILE_ptr'; 388 if res == 'int' and name == 'options': 389 if module == 'parser' or module == 'xmlreader': 390 res = 'parseroptions' 391 392 return res 393 394known_param_types = [] 395 396def is_known_param_type(name): 397 for type in known_param_types: 398 if type == name: 399 return 1 400 return name[-3:] == 'Ptr' or name[-4:] == '_ptr' 401 402def generate_param_type(name, rtype): 403 global test 404 for type in known_param_types: 405 if type == name: 406 return 407 for type in generated_param_types: 408 if type == name: 409 return 410 411 if name[-3:] == 'Ptr' or name[-4:] == '_ptr': 412 define = 0 413 if module in modules_defines: 414 test.write("#ifdef %s\n" % (modules_defines[module])) 415 define = 1 416 test.write(""" 417#define gen_nb_%s 1 418#define gen_%s(no, nr) NULL 419#define des_%s(no, val, nr) 420""" % (name, name, name)) 421 if define == 1: 422 test.write("#endif\n\n") 423 add_generated_param_type(name) 424 425# 426# Provide the type destructors for the return values 427# 428 429known_return_types = [] 430 431def is_known_return_type(name): 432 for type in known_return_types: 433 if type == name: 434 return 1 435 return 0 436 437# 438# Copy the beginning of the C test program result 439# 440 441try: 442 input = open("testapi.c", "r") 443except: 444 input = open(srcPref + "testapi.c", "r") 445test = open('testapi.c.new', 'w') 446 447def compare_and_save(): 448 global test 449 450 test.close() 451 try: 452 input = open("testapi.c", "r").read() 453 except: 454 input = '' 455 test = open('testapi.c.new', "r").read() 456 if input != test: 457 try: 458 os.system("rm testapi.c; mv testapi.c.new testapi.c") 459 except: 460 os.system("mv testapi.c.new testapi.c") 461 print("Updated testapi.c") 462 else: 463 print("Generated testapi.c is identical") 464 465line = input.readline() 466while line != "": 467 if line == "/* CUT HERE: everything below that line is generated */\n": 468 break; 469 if line[0:15] == "#define gen_nb_": 470 type = line[15:].split()[0] 471 known_param_types.append(type) 472 if line[0:19] == "static void desret_": 473 type = line[19:].split('(')[0] 474 known_return_types.append(type) 475 test.write(line) 476 line = input.readline() 477input.close() 478 479if line == "": 480 print("Could not find the CUT marker in testapi.c skipping generation") 481 test.close() 482 sys.exit(0) 483 484print("Scanned testapi.c: found %d parameters types and %d return types\n" % ( 485 len(known_param_types), len(known_return_types))) 486test.write("/* CUT HERE: everything below that line is generated */\n") 487 488 489# 490# Open the input API description 491# 492doc = libxml2.readFile(srcPref + 'doc/libxml2-api.xml', None, 0) 493if doc == None: 494 print("Failed to load doc/libxml2-api.xml") 495 sys.exit(1) 496ctxt = doc.xpathNewContext() 497 498# 499# Generate a list of all function parameters and select only 500# those used in the api tests 501# 502argtypes = {} 503args = ctxt.xpathEval("/api/symbols/function/arg") 504for arg in args: 505 mod = arg.xpathEval('string(../@file)') 506 func = arg.xpathEval('string(../@name)') 507 if (mod not in skipped_modules) and (func not in skipped_functions): 508 type = arg.xpathEval('string(@type)') 509 if type not in argtypes: 510 argtypes[type] = func 511 512# similarly for return types 513rettypes = {} 514rets = ctxt.xpathEval("/api/symbols/function/return") 515for ret in rets: 516 mod = ret.xpathEval('string(../@file)') 517 func = ret.xpathEval('string(../@name)') 518 if (mod not in skipped_modules) and (func not in skipped_functions): 519 type = ret.xpathEval('string(@type)') 520 if type not in rettypes: 521 rettypes[type] = func 522 523# 524# Generate constructors and return type handling for all enums 525# which are used as function parameters 526# 527enums = ctxt.xpathEval("/api/symbols/typedef[@type='enum']") 528for enum in enums: 529 module = enum.xpathEval('string(@file)') 530 name = enum.xpathEval('string(@name)') 531 # 532 # Skip any enums which are not in our filtered lists 533 # 534 if (name == None) or ((name not in argtypes) and (name not in rettypes)): 535 continue; 536 define = 0 537 538 if (name in argtypes) and is_known_param_type(name) == 0: 539 values = ctxt.xpathEval("/api/symbols/enum[@type='%s']" % name) 540 i = 0 541 vals = [] 542 for value in values: 543 vname = value.xpathEval('string(@name)') 544 if vname == None: 545 continue; 546 i = i + 1 547 if i >= 5: 548 break; 549 vals.append(vname) 550 if vals == []: 551 print("Didn't find any value for enum %s" % (name)) 552 continue 553 if module in modules_defines: 554 test.write("#ifdef %s\n" % (modules_defines[module])) 555 define = 1 556 test.write("#define gen_nb_%s %d\n" % (name, len(vals))) 557 test.write("""static %s gen_%s(int no, int nr ATTRIBUTE_UNUSED) {\n""" % 558 (name, name)) 559 i = 1 560 for value in vals: 561 test.write(" if (no == %d) return(%s);\n" % (i, value)) 562 i = i + 1 563 test.write(""" return(0); 564} 565 566static void des_%s(int no ATTRIBUTE_UNUSED, %s val ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { 567} 568 569""" % (name, name)); 570 known_param_types.append(name) 571 572 if (is_known_return_type(name) == 0) and (name in rettypes): 573 if define == 0 and (module in modules_defines): 574 test.write("#ifdef %s\n" % (modules_defines[module])) 575 define = 1 576 test.write("""static void desret_%s(%s val ATTRIBUTE_UNUSED) { 577} 578 579""" % (name, name)) 580 known_return_types.append(name) 581 if define == 1: 582 test.write("#endif\n\n") 583 584# 585# Load the interfaces 586# 587headers = ctxt.xpathEval("/api/files/file") 588for file in headers: 589 name = file.xpathEval('string(@name)') 590 if (name == None) or (name == ''): 591 continue 592 593 # 594 # Some module may be skipped because they don't really consists 595 # of user callable APIs 596 # 597 if is_skipped_module(name): 598 continue 599 600 # 601 # do not test deprecated APIs 602 # 603 desc = file.xpathEval('string(description)') 604 if desc.find('DEPRECATED') != -1: 605 print("Skipping deprecated interface %s" % name) 606 continue; 607 608 test.write("#include <libxml/%s.h>\n" % name) 609 modules.append(name) 610 611# 612# Generate the callers signatures 613# 614for module in modules: 615 test.write("static int test_%s(void);\n" % module); 616 617# 618# Generate the top caller 619# 620 621test.write(""" 622/** 623 * testlibxml2: 624 * 625 * Main entry point of the tester for the full libxml2 module, 626 * it calls all the tester entry point for each module. 627 * 628 * Returns the number of error found 629 */ 630static int 631testlibxml2(void) 632{ 633 int test_ret = 0; 634 635""") 636 637for module in modules: 638 test.write(" test_ret += test_%s();\n" % module) 639 640test.write(""" 641 printf("Total: %d functions, %d tests, %d errors\\n", 642 function_tests, call_tests, test_ret); 643 return(test_ret); 644} 645 646""") 647 648# 649# How to handle a function 650# 651nb_tests = 0 652 653def generate_test(module, node): 654 global test 655 global nb_tests 656 nb_cond = 0 657 no_gen = 0 658 659 name = node.xpathEval('string(@name)') 660 if is_skipped_function(name): 661 return 662 663 # 664 # check we know how to handle the args and return values 665 # and store the information for the generation 666 # 667 try: 668 args = node.xpathEval("arg") 669 except: 670 args = [] 671 t_args = [] 672 n = 0 673 for arg in args: 674 n = n + 1 675 rtype = arg.xpathEval("string(@type)") 676 if rtype == 'void': 677 break; 678 info = arg.xpathEval("string(@info)") 679 nam = arg.xpathEval("string(@name)") 680 type = type_convert(rtype, nam, info, module, name, n) 681 if is_known_param_type(type) == 0: 682 add_missing_type(type, name); 683 no_gen = 1 684 t_args.append((nam, type, rtype, info)) 685 686 try: 687 rets = node.xpathEval("return") 688 except: 689 rets = [] 690 t_ret = None 691 for ret in rets: 692 rtype = ret.xpathEval("string(@type)") 693 info = ret.xpathEval("string(@info)") 694 type = type_convert(rtype, 'return', info, module, name, 0) 695 if rtype == 'void': 696 break 697 if is_known_return_type(type) == 0: 698 add_missing_type(type, name); 699 no_gen = 1 700 t_ret = (type, rtype, info) 701 break 702 703 if no_gen == 0: 704 for t_arg in t_args: 705 (nam, type, rtype, info) = t_arg 706 generate_param_type(type, rtype) 707 708 test.write(""" 709static int 710test_%s(void) { 711 int test_ret = 0; 712 713""" % (name)) 714 715 if no_gen == 1: 716 add_missing_functions(name, module) 717 test.write(""" 718 /* missing type support */ 719 return(test_ret); 720} 721 722""") 723 return 724 725 try: 726 conds = node.xpathEval("cond") 727 for cond in conds: 728 test.write("#if %s\n" % (cond.get_content())) 729 nb_cond = nb_cond + 1 730 except: 731 pass 732 733 define = 0 734 if name in function_defines: 735 test.write("#ifdef %s\n" % (function_defines[name])) 736 define = 1 737 738 # Declare the memory usage counter 739 no_mem = is_skipped_memcheck(name) 740 if no_mem == 0: 741 test.write(" int mem_base;\n"); 742 743 # Declare the return value 744 if t_ret != None: 745 test.write(" %s ret_val;\n" % (t_ret[1])) 746 747 # Declare the arguments 748 for arg in t_args: 749 (nam, type, rtype, info) = arg; 750 # add declaration 751 test.write(" %s %s; /* %s */\n" % (rtype, nam, info)) 752 test.write(" int n_%s;\n" % (nam)) 753 test.write("\n") 754 755 # Cascade loop on of each argument list of values 756 for arg in t_args: 757 (nam, type, rtype, info) = arg; 758 # 759 test.write(" for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % ( 760 nam, nam, type, nam)) 761 762 # log the memory usage 763 if no_mem == 0: 764 test.write(" mem_base = xmlMemBlocks();\n"); 765 766 # prepare the call 767 i = 0; 768 for arg in t_args: 769 (nam, type, rtype, info) = arg; 770 # 771 test.write(" %s = gen_%s(n_%s, %d);\n" % (nam, type, nam, i)) 772 i = i + 1; 773 774 # add checks to avoid out-of-bounds array access 775 i = 0; 776 for arg in t_args: 777 (nam, type, rtype, info) = arg; 778 # assume that "size", "len", and "start" parameters apply to either 779 # the nearest preceding or following char pointer 780 if type == "int" and (nam == "size" or nam == "len" or nam == "start"): 781 for j in (list(range(i - 1, -1, -1)) + list(range(i + 1, len(t_args)))): 782 (bnam, btype) = t_args[j][:2] 783 if btype == "const_char_ptr" or btype == "const_xmlChar_ptr": 784 test.write( 785 " if ((%s != NULL) &&\n" 786 " (%s > xmlStrlen(BAD_CAST %s)))\n" 787 " %s = 0;\n" 788 % (bnam, nam, bnam, nam)) 789 break 790 i = i + 1; 791 792 # do the call, and clanup the result 793 if name in extra_pre_call: 794 test.write(" %s\n"% (extra_pre_call[name])) 795 if t_ret != None: 796 test.write("\n ret_val = %s(" % (name)) 797 need = 0 798 for arg in t_args: 799 (nam, type, rtype, info) = arg 800 if need: 801 test.write(", ") 802 else: 803 need = 1 804 test.write("%s" % nam); 805 test.write(");\n") 806 if name in extra_post_call: 807 test.write(" %s\n"% (extra_post_call[name])) 808 test.write(" desret_%s(ret_val);\n" % t_ret[0]) 809 else: 810 test.write("\n %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 823 test.write(" call_tests++;\n"); 824 825 # Free the arguments 826 i = 0; 827 for arg in t_args: 828 (nam, type, rtype, info) = arg; 829 # This is a hack to prevent generating a destructor for the 830 # 'input' argument in xmlTextReaderSetup. There should be 831 # a better, more generic way to do this! 832 if info.find('destroy') == -1: 833 test.write(" des_%s(n_%s, " % (type, nam)) 834 test.write("%s, %d);\n" % (nam, i)) 835 i = i + 1; 836 837 test.write(" xmlResetLastError();\n"); 838 # Check the memory usage 839 if no_mem == 0: 840 test.write(""" if (mem_base != xmlMemBlocks()) { 841 printf("Leak of %%d blocks found in %s", 842\t xmlMemBlocks() - mem_base); 843\t test_ret++; 844""" % (name)); 845 for arg in t_args: 846 (nam, type, rtype, info) = arg; 847 test.write(""" printf(" %%d", n_%s);\n""" % (nam)) 848 test.write(""" printf("\\n");\n""") 849 test.write(" }\n") 850 851 for arg in t_args: 852 test.write(" }\n") 853 854 test.write(" function_tests++;\n") 855 # 856 # end of conditional 857 # 858 while nb_cond > 0: 859 test.write("#endif\n") 860 nb_cond = nb_cond -1 861 if define == 1: 862 test.write("#endif\n") 863 864 nb_tests = nb_tests + 1; 865 866 test.write(""" 867 return(test_ret); 868} 869 870""") 871 872# 873# Generate all module callers 874# 875for module in modules: 876 # gather all the functions exported by that module 877 try: 878 functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module)) 879 except: 880 print("Failed to gather functions from module %s" % (module)) 881 continue; 882 883 # iterate over all functions in the module generating the test 884 i = 0 885 nb_tests_old = nb_tests 886 for function in functions: 887 i = i + 1 888 generate_test(module, function); 889 890 # header 891 test.write("""static int 892test_%s(void) { 893 int test_ret = 0; 894 895 if (quiet == 0) printf("Testing %s : %d of %d functions ...\\n"); 896""" % (module, module, nb_tests - nb_tests_old, i)) 897 898 # iterate over all functions in the module generating the call 899 for function in functions: 900 name = function.xpathEval('string(@name)') 901 if is_skipped_function(name): 902 continue 903 test.write(" test_ret += test_%s();\n" % (name)) 904 905 # footer 906 test.write(""" 907 if (test_ret != 0) 908\tprintf("Module %s: %%d errors\\n", test_ret); 909 return(test_ret); 910} 911""" % (module)) 912 913# 914# Generate direct module caller 915# 916test.write("""static int 917test_module(const char *module) { 918"""); 919for module in modules: 920 test.write(""" if (!strcmp(module, "%s")) return(test_%s());\n""" % ( 921 module, module)) 922test.write(""" return(0); 923} 924"""); 925 926print("Generated test for %d modules and %d functions" %(len(modules), nb_tests)) 927 928compare_and_save() 929 930missing_list = [] 931for missing in missing_types.keys(): 932 if missing == 'va_list' or missing == '...': 933 continue; 934 935 n = len(missing_types[missing]) 936 missing_list.append((n, missing)) 937 938missing_list.sort(key=lambda a: a[0]) 939print("Missing support for %d functions and %d types see missing.lst" % (missing_functions_nr, len(missing_list))) 940lst = open("missing.lst", "w") 941lst.write("Missing support for %d types" % (len(missing_list))) 942lst.write("\n") 943for miss in missing_list: 944 lst.write("%s: %d :" % (miss[1], miss[0])) 945 i = 0 946 for n in missing_types[miss[1]]: 947 i = i + 1 948 if i > 5: 949 lst.write(" ...") 950 break 951 lst.write(" %s" % (n)) 952 lst.write("\n") 953lst.write("\n") 954lst.write("\n") 955lst.write("Missing support per module"); 956for module in missing_functions.keys(): 957 lst.write("module %s:\n %s\n" % (module, missing_functions[module])) 958 959lst.close() 960 961 962