blob: 1468b87bf38b8adf392956a358dffc32ed69ad09 [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 Veillarda03e3652004-11-02 18:45:30 +0000237 "xmlParserCtxtPtr", "xmlDocPtr", "filepath", "fileoutput" ,
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000238 "xmlNodePtr", "xmlNodePtr_in", "userdata", "xmlChar_ptr" ];
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000239
240def is_known_param_type(name):
241 for type in known_param_types:
242 if type == name:
243 return 1
244 return 0
245
246test.write("""
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000247#define gen_nb_userdata 3
248
249static void *gen_userdata(int no) {
250 if (no == 0) return((void *) &call_tests);
251 if (no == 1) return((void *) -1);
252 return(NULL);
253}
254static void des_userdata(int no ATTRIBUTE_UNUSED, void *val ATTRIBUTE_UNUSED) {
255}
256
257
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000258#define gen_nb_int 4
259
260static int gen_int(int no) {
261 if (no == 0) return(0);
262 if (no == 1) return(1);
263 if (no == 2) return(122);
264 return(-1);
265}
266
267static void des_int(int no ATTRIBUTE_UNUSED, int val ATTRIBUTE_UNUSED) {
268}
269
270#define gen_nb_const_char_ptr 4
271
272static const char *gen_const_char_ptr(int no) {
273 if (no == 0) return("foo");
274 if (no == 1) return("<foo/>");
275 if (no == 2) return("test/ent2");
276 return(NULL);
277}
278static void des_const_char_ptr(int no ATTRIBUTE_UNUSED, const char *val ATTRIBUTE_UNUSED) {
279}
280
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000281#define gen_nb_xmlChar_ptr 2
282
283static xmlChar *gen_xmlChar_ptr(int no) {
284 if (no == 0) return(&chartab);
285 return(NULL);
286}
287static void des_xmlChar_ptr(int no ATTRIBUTE_UNUSED, xmlChar *val ATTRIBUTE_UNUSED) {
288}
289
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000290#define gen_nb_const_xmlChar_ptr 5
291
292static const xmlChar *gen_const_xmlChar_ptr(int no) {
293 if (no == 0) return((const xmlChar *) "foo");
294 if (no == 1) return((const xmlChar *) "<foo/>");
295 if (no == 2) return((const xmlChar *) "nøne");
296 if (no == 3) return((const xmlChar *) " 2ab ");
297 return(NULL);
298}
299static void des_const_xmlChar_ptr(int no ATTRIBUTE_UNUSED, const xmlChar *val ATTRIBUTE_UNUSED) {
300}
301
302#define gen_nb_filepath 8
303
304static const char *gen_filepath(int no) {
305 if (no == 0) return("missing.xml");
306 if (no == 1) return("<foo/>");
307 if (no == 2) return("test/ent2");
308 if (no == 3) return("test/valid/REC-xml-19980210.xml");
309 if (no == 4) return("test/valid/xhtml1-strict.dtd");
310 if (no == 5) return("http://missing.example.org/");
311 if (no == 6) return("http://missing. example.org/");
312 return(NULL);
313}
314static void des_filepath(int no ATTRIBUTE_UNUSED, const char *val ATTRIBUTE_UNUSED) {
315}
316
317#define gen_nb_fileoutput 6
318
319static const char *gen_fileoutput(int no) {
320 if (no == 0) return("/missing.xml");
321 if (no == 1) return("<foo/>");
322 if (no == 2) return("ftp://missing.example.org/foo");
323 if (no == 3) return("http://missing.example.org/");
324 if (no == 4) return("http://missing. example.org/");
325 return(NULL);
326}
327static void des_fileoutput(int no ATTRIBUTE_UNUSED, const char *val ATTRIBUTE_UNUSED) {
328}
329
330#define gen_nb_xmlParserCtxtPtr 2
331static xmlParserCtxtPtr gen_xmlParserCtxtPtr(int no) {
332 if (no == 0) return(xmlNewParserCtxt());
333 return(NULL);
334}
335static void des_xmlParserCtxtPtr(int no ATTRIBUTE_UNUSED, xmlParserCtxtPtr val) {
336 if (val != NULL)
337 xmlFreeParserCtxt(val);
338}
339
Daniel Veillarda03e3652004-11-02 18:45:30 +0000340#define gen_nb_xmlDocPtr 3
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000341static xmlDocPtr gen_xmlDocPtr(int no) {
342 if (no == 0) return(xmlNewDoc(BAD_CAST "1.0"));
Daniel Veillarda03e3652004-11-02 18:45:30 +0000343 if (no == 1) return(xmlReadMemory("<foo/>", 6, "test", NULL, 0));
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000344 return(NULL);
345}
346static void des_xmlDocPtr(int no ATTRIBUTE_UNUSED, xmlDocPtr val) {
347 if (val != NULL)
348 xmlFreeDoc(val);
349}
350
Daniel Veillarda03e3652004-11-02 18:45:30 +0000351#define gen_nb_xmlNodePtr 2
352static xmlNodePtr gen_xmlNodePtr(int no) {
353 if (no == 0) return(xmlNewPI(BAD_CAST "test", NULL));
354 return(NULL);
355}
356static void des_xmlNodePtr(int no ATTRIBUTE_UNUSED, xmlNodePtr val) {
357 if (val != NULL) {
358 xmlUnlinkNode(val);
359 xmlFreeNode(val);
360 }
361}
362
363#define gen_nb_xmlNodePtr_in 3
364static xmlNodePtr gen_xmlNodePtr_in(int no) {
365 if (no == 0) return(xmlNewPI(BAD_CAST "test", NULL));
366 if (no == 0) return(xmlNewText(BAD_CAST "text"));
367 return(NULL);
368}
369static void des_xmlNodePtr_in(int no ATTRIBUTE_UNUSED, xmlNodePtr val ATTRIBUTE_UNUSED) {
370}
371
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000372""");
373
374#
375# Provide the type destructors for the return values
376#
377
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000378known_return_types = [ "int", "const_char_ptr", "xmlDocPtr", "xmlNodePtr",
379 "xmlChar_ptr" ];
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000380
381def is_known_return_type(name):
382 for type in known_return_types:
383 if type == name:
384 return 1
385 return 0
386
387test.write("""
388static void desret_int(int val ATTRIBUTE_UNUSED) {
389}
390static void desret_const_char_ptr(const char *val ATTRIBUTE_UNUSED) {
391}
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000392static void desret_xmlChar_ptr(xmlChar *val) {
393 if (val != NULL)
394 xmlFree(val);
395}
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000396static void desret_xmlDocPtr(xmlDocPtr val) {
397 xmlFreeDoc(val);
398}
399static void desret_xmlNodePtr(xmlNodePtr val) {
400 xmlUnlinkNode(val);
401 xmlFreeNode(val);
402}
403""");
404
405#
406# Generate the top caller
407#
408
409test.write("""
410/**
411 * testlibxml2:
412 *
413 * Main entry point of the tester for the full libxml2 module,
414 * it calls all the tester entry point for each module.
415 *
416 * Returns the number of error found
417 */
418static int
419testlibxml2(void)
420{
421 int ret = 0;
422
423""")
424
425for module in modules:
426 test.write(" ret += test_%s();\n" % module)
427
428test.write("""
429 printf("Total: %d tests, %d errors\\n", call_tests, ret);
430 return(ret);
431}
432
433""")
434
435#
436# How to handle a function
437#
438nb_tests = 0
439
440def generate_test(module, node):
441 global test
442 global nb_tests
443 nb_cond = 0
444 no_gen = 0
445
446 name = node.xpathEval('string(@name)')
447 if is_skipped_function(name):
448 return
449
450 test.write("""
451static int
452test_%s(void) {
453 int ret = 0;
454
455""" % (name))
456
457 #
458 # check we know how to handle the args and return values
459 # and store the informations for the generation
460 #
461 try:
462 args = node.xpathEval("arg")
463 except:
464 args = []
465 t_args = []
Daniel Veillarda03e3652004-11-02 18:45:30 +0000466 n = 0
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000467 for arg in args:
Daniel Veillarda03e3652004-11-02 18:45:30 +0000468 n = n + 1
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000469 rtype = arg.xpathEval("string(@type)")
470 if rtype == 'void':
471 break;
472 info = arg.xpathEval("string(@info)")
473 nam = arg.xpathEval("string(@name)")
Daniel Veillarda03e3652004-11-02 18:45:30 +0000474 type = type_convert(rtype, nam, info, module, name, n)
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000475 if is_known_param_type(type) == 0:
476 add_missing_type(type, name);
477 no_gen = 1
478 t_args.append((nam, type, rtype, info))
479
480 try:
481 rets = node.xpathEval("return")
482 except:
483 rets = []
484 t_ret = None
485 for ret in rets:
486 rtype = ret.xpathEval("string(@type)")
487 info = ret.xpathEval("string(@info)")
Daniel Veillarda03e3652004-11-02 18:45:30 +0000488 type = type_convert(rtype, 'return', info, module, name, 0)
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000489 if rtype == 'void':
490 break
491 if is_known_return_type(type) == 0:
492 add_missing_type(type, name);
493 no_gen = 1
494 t_ret = (type, rtype, info)
495 break
496
497 if no_gen == 1:
498 test.write("""
499 /* missing type support */
500 return(ret);
501}
502
503""")
504 return
505
506 try:
507 conds = node.xpathEval("cond")
508 for cond in conds:
509 test.write("#ifdef %s\n" % (cond.get_content()))
510 nb_cond = nb_cond + 1
511 except:
512 pass
513
514 # Declare the memory usage counter
515 no_mem = is_skipped_memcheck(name)
516 if no_mem == 0:
517 test.write(" int mem_base;\n");
518
519 # Declare the return value
520 if t_ret != None:
521 test.write(" %s ret_val;\n" % (t_ret[1]))
522
523 # Declare the arguments
524 for arg in t_args:
525 (nam, type, rtype, info) = arg;
526 # add declaration
527 test.write(" %s %s; /* %s */\n" % (rtype, nam, info))
528 test.write(" int n_%s;\n" % (nam))
529 test.write("\n")
530
531 # Cascade loop on of each argument list of values
532 for arg in t_args:
533 (nam, type, rtype, info) = arg;
534 #
535 test.write(" for (n_%s = 0;n_%s < gen_nb_%s;n_%s++) {\n" % (
536 nam, nam, type, nam))
537
538 # log the memory usage
539 if no_mem == 0:
540 test.write(" mem_base = xmlMemBlocks();\n");
541
542 # prepare the call
543 for arg in t_args:
544 (nam, type, rtype, info) = arg;
545 #
546 test.write(" %s = gen_%s(n_%s);\n" % (nam, type, nam))
547
548 # do the call, and clanup the result
549 if t_ret != None:
550 test.write("\n ret_val = %s(" % (name))
551 need = 0
552 for arg in t_args:
553 (nam, type, rtype, info) = arg
554 if need:
555 test.write(", ")
556 else:
557 need = 1
558 test.write("%s" % nam);
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000559 test.write(");\n")
560 if extra_post_call.has_key(name):
561 test.write(" %s\n"% (extra_post_call[name]))
562 test.write(" desret_%s(ret_val);\n" % t_ret[0])
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000563 else:
564 test.write("\n %s(" % (name));
565 need = 0;
566 for arg in t_args:
567 (nam, type, rtype, info) = arg;
568 if need:
569 test.write(", ")
570 else:
571 need = 1
572 test.write("%s" % nam)
573 test.write(");\n")
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000574 if extra_post_call.has_key(name):
575 test.write(" %s\n"% (extra_post_call[name]))
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000576
Daniel Veillard8a32fe42004-11-02 22:10:16 +0000577 test.write(" call_tests++;\n");
Daniel Veillarda03e3652004-11-02 18:45:30 +0000578
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000579 # Free the arguments
580 for arg in t_args:
581 (nam, type, rtype, info) = arg;
582 #
583 test.write(" des_%s(n_%s, %s);\n" % (type, nam, nam))
584
585 test.write(" xmlResetLastError();\n");
586 # Check the memory usage
587 if no_mem == 0:
588 test.write(""" if (mem_base != xmlMemBlocks()) {
Daniel Veillarda03e3652004-11-02 18:45:30 +0000589 printf("Leak of %%d blocks found in %s",
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000590 xmlMemBlocks() - mem_base);
591 ret++;
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000592""" % (name));
Daniel Veillarda03e3652004-11-02 18:45:30 +0000593 for arg in t_args:
594 (nam, type, rtype, info) = arg;
595 test.write(""" printf(" %%d", n_%s);\n""" % (nam))
596 test.write(""" printf("\\n");\n""")
597 test.write(" }\n")
Daniel Veillard36e5cd52004-11-02 14:52:23 +0000598
599 for arg in t_args:
600 test.write(" }\n")
601
602 #
603 # end of conditional
604 #
605 while nb_cond > 0:
606 test.write("#endif\n")
607 nb_cond = nb_cond -1
608
609 nb_tests = nb_tests + 1;
610
611 test.write("""
612 return(ret);
613}
614
615""")
616
617#
618# Generate all module callers
619#
620for module in modules:
621 # gather all the functions exported by that module
622 try:
623 functions = ctxt.xpathEval("/api/symbols/function[@file='%s']" % (module))
624 except:
625 print "Failed to gather functions from module %s" % (module)
626 continue;
627
628 # iterate over all functions in the module generating the test
629 for function in functions:
630 generate_test(module, function);
631
632 # header
633 test.write("""static int
634test_%s(void) {
635 int ret = 0;
636
637 printf("Testing %s ...\\n");
638""" % (module, module))
639
640 # iterate over all functions in the module generating the call
641 for function in functions:
642 name = function.xpathEval('string(@name)')
643 if is_skipped_function(name):
644 continue
645 test.write(" ret += test_%s();\n" % (name))
646
647 # footer
648 test.write("""
649 if (ret != 0)
650 printf("Module %s: %%d errors\\n", ret);
651 return(ret);
652}
653""" % (module))
654
655print "Generated test for %d modules and %d functions" %(len(modules), nb_tests)
656nr = 0
657miss = 'none'
658for missing in missing_types.keys():
659 n = len(missing_types[missing])
660 if n > nr:
661 miss = missing
662 nr = n
663
664if nr > 0:
665 print "most needed type support: %s %d times" % (miss, nr)
666
667