blob: 45bedfa6c9b389f9ef68be25c5df0093a9deefc9 [file] [log] [blame]
Daniel Veillard36e5cd52004-11-02 14:52:23 +00001#!/usr/bin/python -u
2#
3# generate a tester program for the API
4#
5import sys
6import string
7try:
8 import libxml2
9except:
10 print "libxml2 python bindings not available, skipping testapi.c generation"
11 sys.exit(0)
12
13#
14# Modules we don't want skip in API test
15#
16skipped_modules = [ "SAX", "SAX2", "xlink", "threads", "globals",
17 "xpathInternals", "xmlunicode", "parserInternals", "xmlmemory",
18 "xmlversion", "debugXML" ]
19
20#
21# Some function really need to be skipped for the tests.
22#
23skipped_functions = [ "xmlFdRead", "xmlReadFd", "xmlCtxtReadFd",
Daniel Veillarda03e3652004-11-02 18:45:30 +000024 "htmlFdRead", "htmlReadFd", "htmlCtxtReadFd",
Daniel Veillard8a32fe42004-11-02 22:10:16 +000025 "xmlCleanupParser", "xmlStrcat", "xmlStrncat" ]
Daniel Veillard36e5cd52004-11-02 14:52:23 +000026
27#
28# Those functions have side effect on the global state
29# and hence generate errors on memory allocation tests
30#
31skipped_memcheck = [ "xmlLoadCatalog", "xmlAddEncodingAlias",
32 "xmlSchemaInitTypes", "xmlNanoFTPProxy", "xmlNanoFTPScanProxy",
33 "xmlNanoHTTPScanProxy", "xmlResetLastError", "xmlCatalogConvert",
34 "xmlCatalogRemove", "xmlLoadCatalogs", "xmlCleanupCharEncodingHandlers",
Daniel Veillarda03e3652004-11-02 18:45:30 +000035 "xmlInitCharEncodingHandlers", "xmlCatalogCleanup",
36 "htmlParseFile" # loads the catalogs
37]
38
39#
40# Extra code needed for some test cases
41#
42extra_post_call = {
43 "xmlAddChild":
44 "if (ret_val == NULL) { xmlFreeNode(cur) ; cur = NULL ; }",
45 "xmlAddChildList":
46 "if (ret_val == NULL) { xmlFreeNodeList(cur) ; cur = NULL ; }",
47 "xmlAddSibling":
48 "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
49 "xmlAddNextSibling":
50 "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
51 "xmlAddPrevSibling":
52 "if (ret_val == NULL) { xmlFreeNode(elem) ; elem = NULL ; }",
53 "xmlDocSetRootElement":
54 "if (doc == NULL) { xmlFreeNode(root) ; root = NULL ; }",
55 "xmlReplaceNode":
56 """if ((old == NULL) || (old->parent == NULL)) {
57 xmlFreeNode(cur) ; cur = NULL ; }""",
58 "xmlTextMerge":
59 """if ((first != NULL) && (first->type != XML_TEXT_NODE)) {
60 xmlFreeNode(second) ; second = NULL ; }""",
Daniel Veillard8a32fe42004-11-02 22:10:16 +000061 "xmlBuildQName":
62 """if ((ret_val != NULL) && (ret_val != ncname) &&
63 (ret_val != prefix) && (ret_val != memory))
64 xmlFree(ret_val);
65 ret_val = NULL;""",
Daniel Veillarda03e3652004-11-02 18:45:30 +000066}
Daniel Veillard36e5cd52004-11-02 14:52:23 +000067
68modules = []
69
70def is_skipped_module(name):
71 for mod in skipped_modules:
72 if mod == name:
73 return 1
74 return 0
75
76def is_skipped_function(name):
77 for fun in skipped_functions:
78 if fun == name:
79 return 1
80 # Do not test destructors
81 if string.find(name, 'Free') != -1:
82 return 1
83 return 0
84
85def is_skipped_memcheck(name):
86 for fun in skipped_memcheck:
87 if fun == name:
88 return 1
89 return 0
90
91missing_types = {}
92def add_missing_type(name, func):
93 try:
94 list = missing_types[name]
95 list.append(func)
96 except:
97 missing_types[name] = [func]
98
99#
100# Open the input API description and the C test program result
101#
102doc = libxml2.readFile('doc/libxml2-api.xml', None, 0)
103if doc == None:
104 print "Failed to load doc/libxml2-api.xml"
105 sys.exit(1)
106test = open('testapi.c', 'w')
107ctxt = doc.xpathNewContext()
108headers = ctxt.xpathEval("/api/files/file")
109
110#
111# Generate the test header
112#
113test.write("""/*
114 * testapi.c: libxml2 API tester program.
115 *
116 * Automatically generated by gentest.py from libxml2-api.xml
117 *
118 * See Copyright for the status of this software.
119 *
120 * daniel@veillard.com
121 */
122
123#include <stdio.h>
124#include <libxml/xmlerror.h>
125
126static int testlibxml2(void);
127
128static int generic_errors = 0;
129static int call_tests = 0;
130
Daniel Veillard348636d2004-11-02 22:34:52 +0000131static xmlChar chartab[1024] = " chartab\\n";
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000132
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000133static void
134structured_errors(void *userData ATTRIBUTE_UNUSED,
135 xmlErrorPtr error ATTRIBUTE_UNUSED) {
136 generic_errors++;
137}
138
139int main(void) {
140 int ret;
141 int blocks, mem;
142
Daniel Veillarda03e3652004-11-02 18:45:30 +0000143 xmlInitParser();
144
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000145 LIBXML_TEST_VERSION
146
147 xmlSetStructuredErrorFunc(NULL, structured_errors);
148
149 ret = testlibxml2();
150
151 xmlCleanupParser();
152 blocks = xmlMemBlocks();
153 mem = xmlMemUsed();
154 if ((blocks != 0) || (mem != 0)) {
155 printf("testapi leaked %d bytes in %d blocks\\n", mem, blocks);
156 }
157 xmlMemoryDump();
158
159 return (ret != 0);
160}
161
162""");
163
164#
165# Load the interfaces
166#
167for file in headers:
168 name = file.xpathEval('string(@name)')
169 if (name == None) or (name == ''):
170 continue
171
172 #
173 # do not test deprecated APIs
174 #
175 desc = file.xpathEval('string(description)')
176 if string.find(desc, 'DEPRECATED') != -1:
177 print "Skipping deprecated interface %s" % name
178 continue;
179
180 #
181 # Some module may be skipped because they don't really consists
182 # of user callable APIs
183 #
184 if is_skipped_module(name):
185 continue
186
187 test.write("#include <libxml/%s.h>\n" % name)
188 modules.append(name)
189
190#
191# Generate the callers signatures
192#
193for module in modules:
194 test.write("static int test_%s(void);\n" % module);
195
196#
197# Provide the type generators and destructors for the parameters
198#
199
Daniel Veillarda03e3652004-11-02 18:45:30 +0000200def type_convert(str, name, info, module, function, pos):
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000201 res = string.replace(str, " *", "_ptr")
202 res = string.replace(res, " ", "_")
Daniel Veillarda03e3652004-11-02 18:45:30 +0000203 res = string.replace(res, "htmlNode", "xmlNode")
204 res = string.replace(res, "htmlDoc", "xmlDoc")
205 res = string.replace(res, "htmlParser", "xmlParser")
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000206 if res == 'const_char_ptr':
207 if string.find(name, "file") != -1 or \
208 string.find(name, "uri") != -1 or \
209 string.find(name, "URI") != -1 or \
210 string.find(info, "filename") != -1 or \
211 string.find(info, "URI") != -1 or \
212 string.find(info, "URL") != -1:
213 if string.find(function, "Save") != -1:
214 return('fileoutput')
215 return('filepath')
216 if res == 'void_ptr':
217 if module == 'nanoftp' and name == 'ctx':
218 return('xmlNanoFTPCtxtPtr')
219 if module == 'nanohttp' and name == 'ctx':
220 return('xmlNanoHTTPCtxtPtr')
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000221 if string.find(name, "data") != -1:
222 return('userdata');
Daniel Veillarda03e3652004-11-02 18:45:30 +0000223 if res == 'xmlNodePtr' and pos != 0:
224 if (function == 'xmlAddChild' and pos == 2) or \
225 (function == 'xmlAddChildList' and pos == 2) or \
226 (function == 'xmlAddNextSibling' and pos == 2) or \
227 (function == 'xmlAddSibling' and pos == 2) or \
228 (function == 'xmlDocSetRootElement' and pos == 2) or \
229 (function == 'xmlReplaceNode' and pos == 2) or \
230 (function == 'xmlTextMerge') or \
231 (function == 'xmlAddPrevSibling' and pos == 2):
232 return('xmlNodePtr_in');
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000233
234 return res
235
236known_param_types = [ "int", "const_char_ptr", "const_xmlChar_ptr",
Daniel Veillarde43cc572004-11-03 11:50:29 +0000237 "xmlParserCtxtPtr", "xmlDocPtr", "filepath", "fileoutput",
238 "xmlNodePtr", "xmlNodePtr_in", "userdata", "xmlChar_ptr",
239 "xmlTextWriterPtr" ];
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000240
241def is_known_param_type(name):
242 for type in known_param_types:
243 if type == name:
244 return 1
245 return 0
246
247test.write("""
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000248#define gen_nb_userdata 3
249
250static void *gen_userdata(int no) {
251 if (no == 0) return((void *) &call_tests);
252 if (no == 1) return((void *) -1);
253 return(NULL);
254}
255static void des_userdata(int no ATTRIBUTE_UNUSED, void *val ATTRIBUTE_UNUSED) {
256}
257
258
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000259#define gen_nb_int 4
260
261static int gen_int(int no) {
262 if (no == 0) return(0);
263 if (no == 1) return(1);
264 if (no == 2) return(122);
265 return(-1);
266}
267
268static void des_int(int no ATTRIBUTE_UNUSED, int val ATTRIBUTE_UNUSED) {
269}
270
271#define gen_nb_const_char_ptr 4
272
273static const char *gen_const_char_ptr(int no) {
274 if (no == 0) return("foo");
275 if (no == 1) return("<foo/>");
276 if (no == 2) return("test/ent2");
277 return(NULL);
278}
279static void des_const_char_ptr(int no ATTRIBUTE_UNUSED, const char *val ATTRIBUTE_UNUSED) {
280}
281
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000282#define gen_nb_xmlChar_ptr 2
283
284static xmlChar *gen_xmlChar_ptr(int no) {
285 if (no == 0) return(&chartab);
286 return(NULL);
287}
288static void des_xmlChar_ptr(int no ATTRIBUTE_UNUSED, xmlChar *val ATTRIBUTE_UNUSED) {
289}
290
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000291#define gen_nb_const_xmlChar_ptr 5
292
293static const xmlChar *gen_const_xmlChar_ptr(int no) {
294 if (no == 0) return((const xmlChar *) "foo");
295 if (no == 1) return((const xmlChar *) "<foo/>");
296 if (no == 2) return((const xmlChar *) "nøne");
297 if (no == 3) return((const xmlChar *) " 2ab ");
298 return(NULL);
299}
300static void des_const_xmlChar_ptr(int no ATTRIBUTE_UNUSED, const xmlChar *val ATTRIBUTE_UNUSED) {
301}
302
303#define gen_nb_filepath 8
304
305static const char *gen_filepath(int no) {
306 if (no == 0) return("missing.xml");
307 if (no == 1) return("<foo/>");
308 if (no == 2) return("test/ent2");
309 if (no == 3) return("test/valid/REC-xml-19980210.xml");
310 if (no == 4) return("test/valid/xhtml1-strict.dtd");
311 if (no == 5) return("http://missing.example.org/");
312 if (no == 6) return("http://missing. example.org/");
313 return(NULL);
314}
315static void des_filepath(int no ATTRIBUTE_UNUSED, const char *val ATTRIBUTE_UNUSED) {
316}
317
318#define gen_nb_fileoutput 6
319
320static const char *gen_fileoutput(int no) {
321 if (no == 0) return("/missing.xml");
322 if (no == 1) return("<foo/>");
323 if (no == 2) return("ftp://missing.example.org/foo");
324 if (no == 3) return("http://missing.example.org/");
325 if (no == 4) return("http://missing. example.org/");
326 return(NULL);
327}
328static void des_fileoutput(int no ATTRIBUTE_UNUSED, const char *val ATTRIBUTE_UNUSED) {
329}
330
331#define gen_nb_xmlParserCtxtPtr 2
332static xmlParserCtxtPtr gen_xmlParserCtxtPtr(int no) {
333 if (no == 0) return(xmlNewParserCtxt());
334 return(NULL);
335}
336static void des_xmlParserCtxtPtr(int no ATTRIBUTE_UNUSED, xmlParserCtxtPtr val) {
337 if (val != NULL)
338 xmlFreeParserCtxt(val);
339}
340
Daniel Veillarda03e3652004-11-02 18:45:30 +0000341#define gen_nb_xmlDocPtr 3
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000342static xmlDocPtr gen_xmlDocPtr(int no) {
343 if (no == 0) return(xmlNewDoc(BAD_CAST "1.0"));
Daniel Veillarda03e3652004-11-02 18:45:30 +0000344 if (no == 1) return(xmlReadMemory("<foo/>", 6, "test", NULL, 0));
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000345 return(NULL);
346}
347static void des_xmlDocPtr(int no ATTRIBUTE_UNUSED, xmlDocPtr val) {
348 if (val != NULL)
349 xmlFreeDoc(val);
350}
351
Daniel Veillarda03e3652004-11-02 18:45:30 +0000352#define gen_nb_xmlNodePtr 2
353static xmlNodePtr gen_xmlNodePtr(int no) {
354 if (no == 0) return(xmlNewPI(BAD_CAST "test", NULL));
355 return(NULL);
356}
357static void des_xmlNodePtr(int no ATTRIBUTE_UNUSED, xmlNodePtr val) {
358 if (val != NULL) {
359 xmlUnlinkNode(val);
360 xmlFreeNode(val);
361 }
362}
363
364#define gen_nb_xmlNodePtr_in 3
365static xmlNodePtr gen_xmlNodePtr_in(int no) {
366 if (no == 0) return(xmlNewPI(BAD_CAST "test", NULL));
367 if (no == 0) return(xmlNewText(BAD_CAST "text"));
368 return(NULL);
369}
370static void des_xmlNodePtr_in(int no ATTRIBUTE_UNUSED, xmlNodePtr val ATTRIBUTE_UNUSED) {
371}
372
Daniel Veillarde43cc572004-11-03 11:50:29 +0000373#define gen_nb_xmlTextWriterPtr 2
374static xmlTextWriterPtr gen_xmlTextWriterPtr(int no) {
375 if (no == 0) return(xmlNewTextWriterFilename("test.out", 0));
376 return(NULL);
377}
378static void des_xmlTextWriterPtr(int no ATTRIBUTE_UNUSED, xmlTextWriterPtr val) {
379 if (val != NULL) xmlFreeTextWriter(val);
380}
381
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000382""");
383
384#
385# Provide the type destructors for the return values
386#
387
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000388known_return_types = [ "int", "const_char_ptr", "xmlDocPtr", "xmlNodePtr",
389 "xmlChar_ptr" ];
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000390
391def is_known_return_type(name):
392 for type in known_return_types:
393 if type == name:
394 return 1
395 return 0
396
397test.write("""
398static void desret_int(int val ATTRIBUTE_UNUSED) {
399}
400static void desret_const_char_ptr(const char *val ATTRIBUTE_UNUSED) {
401}
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000402static void desret_xmlChar_ptr(xmlChar *val) {
403 if (val != NULL)
404 xmlFree(val);
405}
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000406static void desret_xmlDocPtr(xmlDocPtr val) {
407 xmlFreeDoc(val);
408}
409static void desret_xmlNodePtr(xmlNodePtr val) {
410 xmlUnlinkNode(val);
411 xmlFreeNode(val);
412}
413""");
414
415#
416# Generate the top caller
417#
418
419test.write("""
420/**
421 * testlibxml2:
422 *
423 * Main entry point of the tester for the full libxml2 module,
424 * it calls all the tester entry point for each module.
425 *
426 * Returns the number of error found
427 */
428static int
429testlibxml2(void)
430{
431 int ret = 0;
432
433""")
434
435for module in modules:
436 test.write(" ret += test_%s();\n" % module)
437
438test.write("""
439 printf("Total: %d tests, %d errors\\n", call_tests, ret);
440 return(ret);
441}
442
443""")
444
445#
446# How to handle a function
447#
448nb_tests = 0
449
450def generate_test(module, node):
451 global test
452 global nb_tests
453 nb_cond = 0
454 no_gen = 0
455
456 name = node.xpathEval('string(@name)')
457 if is_skipped_function(name):
458 return
459
460 test.write("""
461static int
462test_%s(void) {
463 int ret = 0;
464
465""" % (name))
466
467 #
468 # check we know how to handle the args and return values
469 # and store the informations for the generation
470 #
471 try:
472 args = node.xpathEval("arg")
473 except:
474 args = []
475 t_args = []
Daniel Veillarda03e3652004-11-02 18:45:30 +0000476 n = 0
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000477 for arg in args:
Daniel Veillarda03e3652004-11-02 18:45:30 +0000478 n = n + 1
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000479 rtype = arg.xpathEval("string(@type)")
480 if rtype == 'void':
481 break;
482 info = arg.xpathEval("string(@info)")
483 nam = arg.xpathEval("string(@name)")
Daniel Veillarda03e3652004-11-02 18:45:30 +0000484 type = type_convert(rtype, nam, info, module, name, n)
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000485 if is_known_param_type(type) == 0:
486 add_missing_type(type, name);
487 no_gen = 1
488 t_args.append((nam, type, rtype, info))
489
490 try:
491 rets = node.xpathEval("return")
492 except:
493 rets = []
494 t_ret = None
495 for ret in rets:
496 rtype = ret.xpathEval("string(@type)")
497 info = ret.xpathEval("string(@info)")
Daniel Veillarda03e3652004-11-02 18:45:30 +0000498 type = type_convert(rtype, 'return', info, module, name, 0)
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000499 if rtype == 'void':
500 break
501 if is_known_return_type(type) == 0:
502 add_missing_type(type, name);
503 no_gen = 1
504 t_ret = (type, rtype, info)
505 break
506
507 if no_gen == 1:
508 test.write("""
509 /* missing type support */
510 return(ret);
511}
512
513""")
514 return
515
516 try:
517 conds = node.xpathEval("cond")
518 for cond in conds:
519 test.write("#ifdef %s\n" % (cond.get_content()))
520 nb_cond = nb_cond + 1
521 except:
522 pass
523
524 # Declare the memory usage counter
525 no_mem = is_skipped_memcheck(name)
526 if no_mem == 0:
527 test.write(" int mem_base;\n");
528
529 # Declare the return value
530 if t_ret != None:
531 test.write(" %s ret_val;\n" % (t_ret[1]))
532
533 # Declare the arguments
534 for arg in t_args:
535 (nam, type, rtype, info) = arg;
536 # add declaration
537 test.write(" %s %s; /* %s */\n" % (rtype, nam, info))
538 test.write(" int n_%s;\n" % (nam))
539 test.write("\n")
540
541 # Cascade loop on of each argument list of values
542 for arg in t_args:
543 (nam, type, rtype, info) = arg;
544 #
545 test.write(" for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % (
546 nam, nam, type, nam))
547
548 # log the memory usage
549 if no_mem == 0:
550 test.write(" mem_base = xmlMemBlocks();\n");
551
552 # prepare the call
553 for arg in t_args:
554 (nam, type, rtype, info) = arg;
555 #
556 test.write(" %s = gen_%s(n_%s);\n" % (nam, type, nam))
557
558 # do the call, and clanup the result
559 if t_ret != None:
560 test.write("\n ret_val = %s(" % (name))
561 need = 0
562 for arg in t_args:
563 (nam, type, rtype, info) = arg
564 if need:
565 test.write(", ")
566 else:
567 need = 1
568 test.write("%s" % nam);
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000569 test.write(");\n")
570 if extra_post_call.has_key(name):
571 test.write(" %s\n"% (extra_post_call[name]))
572 test.write(" desret_%s(ret_val);\n" % t_ret[0])
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000573 else:
574 test.write("\n %s(" % (name));
575 need = 0;
576 for arg in t_args:
577 (nam, type, rtype, info) = arg;
578 if need:
579 test.write(", ")
580 else:
581 need = 1
582 test.write("%s" % nam)
583 test.write(");\n")
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000584 if extra_post_call.has_key(name):
585 test.write(" %s\n"% (extra_post_call[name]))
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000586
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000587 test.write(" call_tests++;\n");
Daniel Veillarda03e3652004-11-02 18:45:30 +0000588
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000589 # Free the arguments
590 for arg in t_args:
591 (nam, type, rtype, info) = arg;
592 #
593 test.write(" des_%s(n_%s, %s);\n" % (type, nam, nam))
594
595 test.write(" xmlResetLastError();\n");
596 # Check the memory usage
597 if no_mem == 0:
598 test.write(""" if (mem_base != xmlMemBlocks()) {
Daniel Veillarda03e3652004-11-02 18:45:30 +0000599 printf("Leak of %%d blocks found in %s",
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000600 xmlMemBlocks() - mem_base);
601 ret++;
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000602""" % (name));
Daniel Veillarda03e3652004-11-02 18:45:30 +0000603 for arg in t_args:
604 (nam, type, rtype, info) = arg;
605 test.write(""" printf(" %%d", n_%s);\n""" % (nam))
606 test.write(""" printf("\\n");\n""")
607 test.write(" }\n")
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000608
609 for arg in t_args:
610 test.write(" }\n")
611
612 #
613 # end of conditional
614 #
615 while nb_cond > 0:
616 test.write("#endif\n")
617 nb_cond = nb_cond -1
618
619 nb_tests = nb_tests + 1;
620
621 test.write("""
622 return(ret);
623}
624
625""")
626
627#
628# Generate all module callers
629#
630for module in modules:
631 # gather all the functions exported by that module
632 try:
633 functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
634 except:
635 print "Failed to gather functions from module %s" % (module)
636 continue;
637
638 # iterate over all functions in the module generating the test
639 for function in functions:
640 generate_test(module, function);
641
642 # header
643 test.write("""static int
644test_%s(void) {
645 int ret = 0;
646
647 printf("Testing %s ...\\n");
648""" % (module, module))
649
650 # iterate over all functions in the module generating the call
651 for function in functions:
652 name = function.xpathEval('string(@name)')
653 if is_skipped_function(name):
654 continue
655 test.write(" ret += test_%s();\n" % (name))
656
657 # footer
658 test.write("""
659 if (ret != 0)
660 printf("Module %s: %%d errors\\n", ret);
661 return(ret);
662}
663""" % (module))
664
665print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
666nr = 0
667miss = 'none'
668for missing in missing_types.keys():
669 n = len(missing_types[missing])
670 if n > nr:
671 miss = missing
672 nr = n
673
674if nr > 0:
675 print "most needed type support: %s %d times" % (miss, nr)
676
677