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