1 /*
2 tester - liblinphone test suite
3 Copyright (C) 2013 Belledonne Communications SARL
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <bctoolbox/tester.h>
24
25 #include <stdlib.h>
26 #include <time.h>
27 #include <stdio.h>
28
29 #ifndef _WIN32
30 #include <unistd.h>
31 #endif
32
33 #ifndef IN_BCUNIT_SOURCES
34 #include <BCUnit/Basic.h>
35 #include <BCUnit/Automated.h>
36 #include <BCUnit/MyMem.h>
37 #include <BCUnit/Util.h>
38 #else
39 #include "Basic.h"
40 #include "Automated.h"
41 #include "MyMem.h"
42 #include "Util.h"
43 #endif
44
45 #ifdef _WIN32
46 #if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP)
47 #define BC_TESTER_WINDOWS_DESKTOP 1
48 #elif defined(WINAPI_FAMILY_PARTITION)
49 #if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
50 #define BC_TESTER_WINDOWS_DESKTOP 1
51 #elif defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
52 #define BC_TESTER_WINDOWS_PHONE 1
53 #elif defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
54 #define BC_TESTER_WINDOWS_UNIVERSAL 1
55 #endif
56 #endif
57 #endif
58
59 #ifdef __linux
60 /*for monitoring total space allocated via malloc*/
61 #include <malloc.h>
62 #endif
63
64 #ifndef F_OK
65 #define F_OK 00 /* Visual Studio does not define F_OK */
66 #endif
67
68 #ifdef HAVE_BCUNIT_CUCURSES_H
69 #define HAVE_CU_CURSES 1
70 #endif
71
72 static char *bc_tester_resource_dir_prefix = NULL;
73 // by default writable will always write near the executable
74 static char *bc_tester_writable_dir_prefix = NULL;
75
76 static char *bc_current_suite_name = NULL;
77 static char *bc_current_test_name = NULL;
78
79 int bc_printf_verbosity_info;
80 int bc_printf_verbosity_error;
81
82 static test_suite_t **test_suite = NULL;
83 static int nb_test_suites = 0;
84
85 #ifdef HAVE_CU_CURSES
86 #include "BCUnit/CUCurses.h"
87 static unsigned char curses = 0;
88 #endif
89
90 char* xml_file = "BCUnitAutomated-Results.xml";
91 int xml_enabled = 0;
92 char * suite_name = NULL;
93 char * test_name = NULL;
94 char * tag_name = NULL;
95 char * expected_res = NULL;
96 static size_t max_vm_kb = 0;
97 int run_skipped_tests = 0;
98
99 static void (*tester_printf_va)(int level, const char *format, va_list args)=NULL;
100
bc_tester_printf(int level,const char * format,...)101 void bc_tester_printf(int level, const char *format, ...) {
102 va_list args;
103 va_start (args, format);
104 if (tester_printf_va) {
105 tester_printf_va(level, format, args);
106 }else{
107 vfprintf(stderr, format, args);
108 }
109 va_end (args);
110 }
111
bc_tester_run_suite(test_suite_t * suite,const char * tag_name)112 int bc_tester_run_suite(test_suite_t *suite, const char *tag_name) {
113 int i;
114 CU_pSuite pSuite;
115
116 if (tag_name != NULL) {
117 size_t j;
118 int nb_tests_for_tag = 0;
119 for (i = 0; i < suite->nb_tests; i++) {
120 for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) {
121 if ((suite->tests[i].tags[j] != NULL) && (strcasecmp(tag_name, suite->tests[i].tags[j]) == 0)) {
122 nb_tests_for_tag++;
123 }
124 }
125 }
126 if (nb_tests_for_tag > 0) {
127 pSuite = CU_add_suite_with_setup_and_teardown(suite->name, suite->before_all, suite->after_all,
128 suite->before_each, suite->after_each);
129 for (i = 0; i < suite->nb_tests; i++) {
130 for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) {
131 if ((suite->tests[i].tags[j] != NULL) && (strcasecmp(tag_name, suite->tests[i].tags[j]) == 0)) {
132 if (NULL == CU_add_test(pSuite, suite->tests[i].name, suite->tests[i].func)) {
133 return CU_get_error();
134 }
135 }
136 }
137 }
138 }
139 } else {
140 pSuite = CU_add_suite_with_setup_and_teardown(suite->name, suite->before_all, suite->after_all,
141 suite->before_each, suite->after_each);
142 for (i = 0; i < suite->nb_tests; i++) {
143 size_t j;
144 bool_t skip = FALSE;
145 for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) {
146 if ((suite->tests[i].tags[j] != NULL) && (strcasecmp("Skip", suite->tests[i].tags[j]) == 0) && (run_skipped_tests == 0)) {
147 skip = TRUE;
148 }
149 }
150 if (!skip) {
151 if (NULL == CU_add_test(pSuite, suite->tests[i].name, suite->tests[i].func)) {
152 return CU_get_error();
153 }
154 }
155 }
156 }
157
158 return 0;
159 }
160
bc_tester_suite_name(int suite_index)161 const char * bc_tester_suite_name(int suite_index) {
162 if (suite_index >= nb_test_suites) return NULL;
163 return test_suite[suite_index]->name;
164 }
165
bc_tester_suite_index(const char * suite_name)166 int bc_tester_suite_index(const char *suite_name) {
167 int i;
168
169 for (i = 0; i < nb_test_suites; i++) {
170 if (strcmp(suite_name, test_suite[i]->name) == 0) {
171 return i;
172 }
173 }
174
175 return -1;
176 }
177
178
bc_tester_test_index(test_suite_t * suite,const char * test_name)179 int bc_tester_test_index(test_suite_t *suite, const char *test_name) {
180 int i;
181
182 for (i = 0; i < suite->nb_tests; i++) {
183 if (strcmp(test_name, suite->tests[i].name) == 0) {
184 return i;
185 }
186 }
187
188 return -1;
189 }
190
bc_tester_nb_suites(void)191 int bc_tester_nb_suites(void) {
192 return nb_test_suites;
193 }
194
bc_tester_test_name(const char * suite_name,int test_index)195 const char * bc_tester_test_name(const char *suite_name, int test_index) {
196 test_suite_t *suite = NULL;
197 size_t j = 0;
198 bool_t skip = FALSE;
199
200 int suite_index = bc_tester_suite_index(suite_name);
201 if ((suite_index < 0) || (suite_index >= nb_test_suites)) return NULL;
202 suite = test_suite[suite_index];
203 if (test_index >= suite->nb_tests) return NULL;
204
205 for (j = 0; j < (sizeof(suite->tests[test_index].tags) / sizeof(suite->tests[test_index].tags[0])); j++) {
206 if ((suite->tests[test_index].tags[j] != NULL) && (strcasecmp("Skip", suite->tests[test_index].tags[j]) == 0) && (run_skipped_tests == 0)) {
207 skip = TRUE;
208 }
209 }
210 if (skip) return NULL;
211
212 return suite->tests[test_index].name;
213 }
214
bc_tester_nb_tests(const char * suite_name)215 int bc_tester_nb_tests(const char *suite_name) {
216 int i = bc_tester_suite_index(suite_name);
217 if (i < 0) return 0;
218 return test_suite[i]->nb_tests;
219 }
220
bc_tester_list_suites(void)221 void bc_tester_list_suites(void) {
222 int j;
223 for(j=0;j<nb_test_suites;j++) {
224 bc_tester_printf(bc_printf_verbosity_info, "%s", bc_tester_suite_name(j));
225 }
226 }
227
bc_tester_list_tests(const char * suite_name)228 void bc_tester_list_tests(const char *suite_name) {
229 int j;
230 for( j = 0; j < bc_tester_nb_tests(suite_name); j++) {
231 const char *test_name = bc_tester_test_name(suite_name, j);
232 if (test_name) {
233 bc_tester_printf(bc_printf_verbosity_info, "%s", test_name);
234 }
235 }
236 }
237
all_complete_message_handler(const CU_pFailureRecord pFailure)238 static void all_complete_message_handler(const CU_pFailureRecord pFailure) {
239 #ifdef HAVE_CU_GET_SUITE
240 char * results = CU_get_run_results_string();
241 bc_tester_printf(bc_printf_verbosity_info,"\n%s",results);
242 CU_FREE(results);
243 #endif
244 }
245
suite_init_failure_message_handler(const CU_pSuite pSuite)246 static void suite_init_failure_message_handler(const CU_pSuite pSuite) {
247 bc_tester_printf(bc_printf_verbosity_error,"Suite initialization failed for [%s]", pSuite->pName);
248 }
249
suite_cleanup_failure_message_handler(const CU_pSuite pSuite)250 static void suite_cleanup_failure_message_handler(const CU_pSuite pSuite) {
251 bc_tester_printf(bc_printf_verbosity_error,"Suite cleanup failed for [%s]", pSuite->pName);
252 }
253
254 #ifdef HAVE_CU_GET_SUITE
255 static uint64_t suite_start_time = 0;
suite_start_message_handler(const CU_pSuite pSuite)256 static void suite_start_message_handler(const CU_pSuite pSuite) {
257 bc_tester_printf(bc_printf_verbosity_info,"Suite [%s] started\n", pSuite->pName);
258 suite_start_time = bctbx_get_cur_time_ms();
259 bc_current_suite_name = pSuite->pName;
260 }
suite_complete_message_handler(const CU_pSuite pSuite,const CU_pFailureRecord pFailure)261 static void suite_complete_message_handler(const CU_pSuite pSuite, const CU_pFailureRecord pFailure) {
262 bc_tester_printf(bc_printf_verbosity_info, "Suite [%s] ended in %.3f sec\n", pSuite->pName,
263 (bctbx_get_cur_time_ms() - suite_start_time) / 1000.f);
264 }
265
266 static uint64_t test_start_time = 0;
test_start_message_handler(const CU_pTest pTest,const CU_pSuite pSuite)267 static void test_start_message_handler(const CU_pTest pTest, const CU_pSuite pSuite) {
268 bc_tester_printf(bc_printf_verbosity_info,"Suite [%s] Test [%s] started", pSuite->pName,pTest->pName);
269 test_start_time = bctbx_get_cur_time_ms();
270 bc_current_test_name = pTest->pName;
271 }
272
273 /*derivated from bcunit*/
test_complete_message_handler(const CU_pTest pTest,const CU_pSuite pSuite,const CU_pFailureRecord pFailureList)274 static void test_complete_message_handler(const CU_pTest pTest, const CU_pSuite pSuite,
275 const CU_pFailureRecord pFailureList) {
276 int i;
277 CU_pFailureRecord pFailure = pFailureList;
278 char *buffer = NULL;
279 char* result = bc_sprintf("Suite [%s] Test [%s] %s in %.3f secs", pSuite->pName, pTest->pName,
280 pFailure ? "failed" : "passed", (bctbx_get_cur_time_ms() - test_start_time) / 1000.f);
281
282 if (pFailure) {
283 for (i = 1; (NULL != pFailure); pFailure = pFailure->pNext, i++) {
284 buffer = bc_sprintf("%s\n %d. %s:%u - %s",
285 result,
286 i,
287 (NULL != pFailure->strFileName) ? pFailure->strFileName : "",
288 pFailure->uiLineNumber,
289 (NULL != pFailure->strCondition) ? pFailure->strCondition : "");
290 free(result);
291 result = buffer;
292 }
293 }
294
295 bc_tester_printf(bc_printf_verbosity_info,"%s", result);
296 free(result);
297
298 //insert empty line
299 bc_tester_printf(bc_printf_verbosity_info,"");
300
301 #ifdef __linux
302 /* use mallinfo() to monitor allocated space. It is linux specific but other methods don't work:
303 * setrlimit() RLIMIT_DATA doesn't count memory allocated via mmap() (which is used internally by malloc)
304 * setrlimit() RLIMIT_AS works but also counts virtual memory allocated by thread stacks, which is very big and
305 * hardly controllable.
306 * setrlimit() RLIMIT_RSS does nothing interesting on linux.
307 * getrusage() of RSS is unreliable: memory blocks can be leaked without being read or written, which would not
308 * appear in RSS.
309 * mallinfo() itself is the less worse solution. Allocated bytes are returned as 'int' so limited to 2GB
310 */
311 if (max_vm_kb) {
312 struct mallinfo minfo = mallinfo();
313 if ((size_t)minfo.uordblks > max_vm_kb * 1024) {
314 bc_tester_printf(
315 bc_printf_verbosity_error,
316 "The program exceeded the maximum amount of memory allocatable (%i bytes), aborting now.\n",
317 minfo.uordblks);
318 abort();
319 }
320 }
321 #endif
322 }
323 #endif
324
bc_tester_run_tests(const char * suite_name,const char * test_name,const char * tag_name)325 int bc_tester_run_tests(const char *suite_name, const char *test_name, const char *tag_name) {
326 int i;
327
328 /* initialize the BCUnit test registry */
329 if (CUE_SUCCESS != CU_initialize_registry())
330 return CU_get_error();
331
332 for (i = 0; i < nb_test_suites; i++) {
333 bc_tester_run_suite(test_suite[i], tag_name);
334 }
335 #ifdef HAVE_CU_GET_SUITE
336 CU_set_suite_start_handler(suite_start_message_handler);
337 CU_set_suite_complete_handler(suite_complete_message_handler);
338 CU_set_test_start_handler(test_start_message_handler);
339 CU_set_test_complete_handler(test_complete_message_handler);
340 #endif
341 CU_set_all_test_complete_handler(all_complete_message_handler);
342 CU_set_suite_init_failure_handler(suite_init_failure_message_handler);
343 CU_set_suite_cleanup_failure_handler(suite_cleanup_failure_message_handler);
344
345 if (xml_enabled == 1) {
346 CU_automated_run_tests();
347 } else {
348
349 #ifndef HAVE_CU_GET_SUITE
350 if( suite_name ){
351 bc_tester_printf(bc_printf_verbosity_info, "Tester compiled without CU_get_suite() function, running all tests instead of suite '%s'", suite_name);
352 }
353 #else
354 if (suite_name){
355 CU_pSuite suite;
356 suite=CU_get_suite(suite_name);
357 if (!suite) {
358 if (tag_name != NULL) {
359 bc_tester_printf(bc_printf_verbosity_error, "Could not find suite '%s' or this suite has no tests with tag '%s'. Available suites are:", suite_name, tag_name);
360 } else {
361 bc_tester_printf(bc_printf_verbosity_error, "Could not find suite '%s'. Available suites are:", suite_name);
362 }
363 bc_tester_list_suites();
364 return -1;
365 } else if (test_name) {
366 CU_pTest test=CU_get_test_by_name(test_name, suite);
367 if (!test) {
368 if (tag_name != NULL) {
369 bc_tester_printf(bc_printf_verbosity_error, "Could not find test '%s' in suite '%s' or this test is not tagged '%s'. Available tests are:", test_name, suite_name, tag_name);
370 } else {
371 bc_tester_printf(bc_printf_verbosity_error, "Could not find test '%s' in suite '%s'. Available tests are:", test_name, suite_name);
372 }
373 // do not use suite_name here, since this method is case sensitive
374 bc_tester_list_tests(suite->pName);
375 return -2;
376 } else {
377 CU_ErrorCode err= CU_run_test(suite, test);
378 if (err != CUE_SUCCESS) bc_tester_printf(bc_printf_verbosity_error, "CU_basic_run_test error %d", err);
379 }
380 } else {
381 CU_run_suite(suite);
382 }
383 }
384 else
385 #endif
386 {
387 #ifdef HAVE_CU_CURSES
388 if (curses) {
389 /* Run tests using the BCUnit curses interface */
390 CU_curses_run_tests();
391 }
392 else
393 #endif
394 {
395 /* Run all tests using the BCUnit Basic interface */
396 CU_run_all_tests();
397 }
398 }
399 }
400 #ifdef __linux
401 bc_tester_printf(bc_printf_verbosity_info, "Still %i kilobytes allocated when all tests are finished.",
402 mallinfo().uordblks / 1024);
403 #endif
404
405 return CU_get_number_of_tests_failed()!=0;
406
407 }
408
409 #if !defined(BC_TESTER_WINDOWS_PHONE) && !defined(BC_TESTER_WINDOWS_UNIVERSAL) && !defined(__QNX__) && !defined(__ANDROID__) && !defined(IOS)
file_exists(const char * root_path)410 static int file_exists(const char* root_path) {
411 char * res_path = bc_sprintf("%s/%s", root_path, expected_res);
412 int err = bctbx_file_exist(res_path);
413 free(res_path);
414 return err == 0;
415 }
416 #endif
417
detect_res_prefix(const char * prog)418 static void detect_res_prefix(const char* prog) {
419 char* progpath = NULL;
420 char* progname = NULL;
421 FILE* writable_file = NULL;
422
423
424 if (prog != NULL) {
425 char *ptr;
426 progpath = strdup(prog);
427 if (strchr(prog, '/') != NULL) {
428 progpath[strrchr(prog, '/') - prog + 1] = '\0';
429 } else if (strchr(prog, '\\') != NULL) {
430 progpath[strrchr(prog, '\\') - prog + 1] = '\0';
431 }
432 ptr = strrchr(prog, '/');
433 if (ptr == NULL) {
434 ptr = strrchr(prog, '\\');
435 }
436 if (ptr != NULL) {
437 #ifdef BC_TESTER_WINDOWS_DESKTOP
438 char *exe = strstr(prog, ".exe");
439 if (exe != NULL) exe[0] = '\0';
440 #endif
441 progname = strdup(ptr + 1);
442 }
443 }
444 #if !defined(BC_TESTER_WINDOWS_PHONE) && !defined(BC_TESTER_WINDOWS_UNIVERSAL) && !defined(__QNX__) && !defined(__ANDROID__) && !defined(IOS)
445 {
446 char* prefix = NULL;
447 char *installed_resources_path = NULL;
448
449 if ((progname != NULL) && (progpath != NULL)) {
450 installed_resources_path = bc_sprintf("%s../share/%s", progpath, progname);
451 }
452
453 if (file_exists(".")) {
454 prefix = strdup(".");
455 } else if (file_exists("..")) {
456 prefix = strdup("..");
457 } else if ((installed_resources_path != NULL) && file_exists(installed_resources_path)) {
458 prefix = strdup(installed_resources_path);
459 } else if (progpath) {
460 //for autotools, binary is in .libs/ subdirectory
461 char * progpath2 = bc_sprintf("%s/../", progpath);
462 if (file_exists(progpath)) {
463 prefix = strdup(progpath);
464 } else if (file_exists(progpath2)) {
465 prefix = strdup(progpath2);
466 }
467 free(progpath2);
468 }
469 if (installed_resources_path != NULL) free(installed_resources_path);
470
471 if (bc_tester_resource_dir_prefix != NULL && !file_exists(bc_tester_resource_dir_prefix)) {
472 bc_tester_printf(bc_printf_verbosity_error, "Invalid provided resource directory: could not find expected resources '%s' in '%s'.", expected_res, bc_tester_resource_dir_prefix);
473 free(bc_tester_resource_dir_prefix);
474 bc_tester_resource_dir_prefix = NULL;
475 }
476
477 if (prefix != NULL) {
478 if (bc_tester_resource_dir_prefix == NULL) {
479 bc_tester_printf(bc_printf_verbosity_error, "Resource directory set to %s", prefix);
480 bc_tester_set_resource_dir_prefix(prefix);
481 }
482
483 if (bc_tester_writable_dir_prefix == NULL) {
484 bc_tester_printf(bc_printf_verbosity_error, "Writable directory set to %s", prefix);
485 bc_tester_set_writable_dir_prefix(prefix);
486 }
487 free(prefix);
488 }
489 }
490 #endif
491
492 // check that we can write in writable directory
493 if (bc_tester_writable_dir_prefix != NULL) {
494 char * writable_file_path = bc_sprintf("%s/%s", bc_tester_writable_dir_prefix, ".bc_tester_utils.tmp");
495 writable_file = fopen(writable_file_path, "w");
496 if (writable_file) {
497 fclose(writable_file);
498 }
499 free(writable_file_path);
500 }
501 if (bc_tester_resource_dir_prefix == NULL || writable_file == NULL) {
502 if (bc_tester_resource_dir_prefix == NULL) {
503 bc_tester_printf(bc_printf_verbosity_error, "Could not find resource directory '%s' in '%s'! Please try again using option --resource-dir.", expected_res, progpath);
504 }
505 if (writable_file == NULL) {
506 bc_tester_printf(bc_printf_verbosity_error, "Failed to write file in %s. Please try again using option --writable-dir.", bc_tester_writable_dir_prefix);
507 }
508 abort();
509 }
510
511 if (progpath != NULL) {
512 free(progpath);
513 }
514 if(progname) {
515 free(progname);
516 }
517 }
518
bc_tester_init(void (* ftester_printf)(int level,const char * format,va_list args),int iverbosity_info,int iverbosity_error,const char * aexpected_res)519 void bc_tester_init(void (*ftester_printf)(int level, const char *format, va_list args), int iverbosity_info, int iverbosity_error, const char* aexpected_res) {
520 tester_printf_va = ftester_printf;
521 bc_printf_verbosity_error = iverbosity_error;
522 bc_printf_verbosity_info = iverbosity_info;
523 if (!bc_tester_writable_dir_prefix) {
524 bc_tester_writable_dir_prefix = strdup(".");
525 }
526 if (aexpected_res) {
527 expected_res = strdup(aexpected_res);
528 }
529 }
530
bc_tester_set_max_vm(size_t amax_vm_kb)531 void bc_tester_set_max_vm(size_t amax_vm_kb) {
532 #ifdef __linux
533 max_vm_kb = (size_t)amax_vm_kb;
534 bc_tester_printf(bc_printf_verbosity_info, "Maximum virtual memory space set to %li kilo bytes", max_vm_kb);
535 #else
536 bc_tester_printf(bc_printf_verbosity_error, "Maximum virtual memory space setting is only implemented on Linux.");
537 #endif
538 }
539
bc_tester_helper(const char * name,const char * additionnal_helper)540 void bc_tester_helper(const char *name, const char* additionnal_helper) {
541 bc_tester_printf(bc_printf_verbosity_info,
542 "%s --help\n"
543 #ifdef HAVE_CU_CURSES
544 "\t\t\t--curses\n"
545 #endif
546 "\t\t\t--list-suites\n"
547 "\t\t\t--list-tests <suite>\n"
548 "\t\t\t--suite <suite name>\n"
549 "\t\t\t--test <test name>\n"
550 "\t\t\t--tag <tag name> (execute all tests with the given tag)\n"
551 "\t\t\t--all (execute all tests, even the ones with the Skip flag)\n"
552 "\t\t\t--resource-dir <folder path> (directory where tester resource are located)\n"
553 "\t\t\t--writable-dir <folder path> (directory where temporary files should be created)\n"
554 "\t\t\t--xml\n"
555 "\t\t\t--xml-file <xml file name>\n"
556 "\t\t\t--max-alloc <size in ko> (maximum amount of memory obtained via malloc allocator)\n"
557 "And additionally:\n"
558 "%s",
559 name,
560 additionnal_helper);
561 }
562
bc_tester_parse_args(int argc,char ** argv,int argid)563 int bc_tester_parse_args(int argc, char **argv, int argid)
564 {
565 int i = argid;
566
567 if (strcmp(argv[i],"--help")==0){
568 return -1;
569 } else if (strcmp(argv[i],"--test")==0){
570 CHECK_ARG("--test", ++i, argc);
571 test_name=argv[i];
572 } else if (strcmp(argv[i],"--suite")==0){
573 CHECK_ARG("--suite", ++i, argc);
574 suite_name=argv[i];
575 } else if (strcmp(argv[i], "--tag") == 0) {
576 CHECK_ARG("--tag", ++i, argc);
577 tag_name = argv[i];
578 } else if (strcmp(argv[i], "--all") == 0) {
579 run_skipped_tests = 1;
580 } else if (strcmp(argv[i],"--list-suites")==0){
581 bc_tester_list_suites();
582 return 0;
583 } else if (strcmp(argv[i],"--list-tests")==0){
584 CHECK_ARG("--list-tests", ++i, argc);
585 suite_name = argv[i];
586 bc_tester_list_tests(suite_name);
587 return 0;
588 } else if (strcmp(argv[i], "--xml-file") == 0){
589 CHECK_ARG("--xml-file", ++i, argc);
590 xml_file = argv[i];
591 xml_enabled = 1;
592 } else if (strcmp(argv[i], "--xml") == 0){
593 xml_enabled = 1;
594 } else if (strcmp(argv[i], "--max-alloc") == 0) {
595 CHECK_ARG("--max-alloc", ++i, argc);
596 max_vm_kb = atol(argv[i]);
597 } else if (strcmp(argv[i], "--resource-dir") == 0) {
598 CHECK_ARG("--resource-dir", ++i, argc);
599 bc_tester_resource_dir_prefix = strdup(argv[i]);
600 } else if (strcmp(argv[i], "--writable-dir") == 0) {
601 CHECK_ARG("--writable-dir", ++i, argc);
602 bc_tester_writable_dir_prefix = strdup(argv[i]);
603 } else {
604 bc_tester_printf(bc_printf_verbosity_error, "Unknown option \"%s\"", argv[i]);
605 return -1;
606 }
607
608 if( xml_enabled && (suite_name || test_name) ){
609 bc_tester_printf(bc_printf_verbosity_error, "Cannot use both XML and specific test suite");
610 return -1;
611 }
612
613 /* returns number of arguments read + 1 */
614 return i - argid + 1;
615 }
616
bc_tester_start(const char * prog_name)617 int bc_tester_start(const char* prog_name) {
618 int ret;
619
620 if (expected_res)
621 detect_res_prefix(prog_name);
622
623 if (max_vm_kb)
624 bc_tester_set_max_vm(max_vm_kb);
625
626 if( xml_enabled ){
627 char * xml_tmp_file = bc_sprintf("%s.tmp", xml_file);
628 CU_set_output_filename(xml_tmp_file);
629 CU_automated_enable_junit_xml(TRUE); /* this requires 3.0.1 because previous versions crash automated.c */
630 free(xml_tmp_file);
631 }
632
633 ret = bc_tester_run_tests(suite_name, test_name, tag_name);
634
635 return ret;
636 }
bc_tester_add_suite(test_suite_t * suite)637 void bc_tester_add_suite(test_suite_t *suite) {
638 if (test_suite == NULL) {
639 test_suite = (test_suite_t **)malloc(10 * sizeof(test_suite_t *));
640 }
641 test_suite[nb_test_suites] = suite;
642 nb_test_suites++;
643 if ((nb_test_suites % 10) == 0) {
644 test_suite = (test_suite_t **)realloc(test_suite, (nb_test_suites + 10) * sizeof(test_suite_t *));
645 }
646 }
647
bc_tester_uninit(void)648 void bc_tester_uninit(void) {
649 /* Redisplay list of failed tests on end */
650 /*BUG: do not display list of failures on mingw, it crashes mysteriously*/
651 #if !defined(_WIN32) && !defined(_MSC_VER)
652 /* Redisplay list of failed tests on end */
653 if (CU_get_number_of_failure_records()){
654 CU_basic_show_failures(CU_get_failure_list());
655 }
656 #endif
657 CU_cleanup_registry();
658 /*add missing final newline*/
659 bc_tester_printf(bc_printf_verbosity_info,"");
660
661 if (xml_enabled) {
662 /*create real xml file only if tester did not crash*/
663 char * xml_tmp_file = bc_sprintf("%s.tmp-Results.xml", xml_file);
664 bc_tester_printf(bc_printf_verbosity_info, "Tests ended, renaming temporary result file %s to %s", xml_tmp_file, xml_file);
665 if (rename(xml_tmp_file, xml_file) != 0) {
666 bc_tester_printf(bc_printf_verbosity_error, "Failed to rename XML file: %s", strerror(errno));
667 }
668 free(xml_tmp_file);
669 }
670
671 if (test_suite != NULL) {
672 free(test_suite);
673 test_suite = NULL;
674 nb_test_suites = 0;
675 }
676
677 if (bc_tester_resource_dir_prefix != NULL) {
678 free(bc_tester_resource_dir_prefix);
679 bc_tester_resource_dir_prefix = NULL;
680 }
681 if (bc_tester_writable_dir_prefix != NULL) {
682 free(bc_tester_writable_dir_prefix);
683 bc_tester_writable_dir_prefix = NULL;
684 }
685 }
686
bc_tester_set_dir_prefix(char ** prefix,const char * name)687 static void bc_tester_set_dir_prefix(char **prefix, const char *name) {
688 if (*prefix != NULL) free(*prefix);
689 *prefix = strdup(name);
690 }
691
bc_tester_get_resource_dir_prefix(void)692 const char * bc_tester_get_resource_dir_prefix(void) {
693 return bc_tester_resource_dir_prefix;
694 }
695
bc_tester_set_resource_dir_prefix(const char * name)696 void bc_tester_set_resource_dir_prefix(const char *name) {
697 bc_tester_set_dir_prefix(&bc_tester_resource_dir_prefix, name);
698 }
699
bc_tester_get_writable_dir_prefix(void)700 const char * bc_tester_get_writable_dir_prefix(void) {
701 return bc_tester_writable_dir_prefix;
702 }
703
bc_tester_set_writable_dir_prefix(const char * name)704 void bc_tester_set_writable_dir_prefix(const char *name) {
705 bc_tester_set_dir_prefix(&bc_tester_writable_dir_prefix, name);
706 }
707
bc_tester_path(const char * prefix,const char * name)708 static char * bc_tester_path(const char *prefix, const char *name) {
709 if (name) {
710 return bc_sprintf("%s/%s", prefix, name);
711 } else {
712 return NULL;
713 }
714 }
715
bc_tester_res(const char * name)716 char * bc_tester_res(const char *name) {
717 return bc_tester_path(bc_tester_resource_dir_prefix, name);
718 }
719
bc_tester_file(const char * name)720 char * bc_tester_file(const char *name) {
721 return bc_tester_path(bc_tester_writable_dir_prefix, name);
722 }
723
bc_sprintfva(const char * format,va_list args)724 char* bc_sprintfva(const char* format, va_list args) {
725 /* Guess we need no more than 100 bytes. */
726 int n, size = 200;
727 char *p,*np;
728 #ifndef _WIN32
729 va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
730 #endif
731 if ((p = malloc(size)) == NULL)
732 return NULL;
733 while (1)
734 {
735 /* Try to print in the allocated space. */
736 #ifndef _WIN32
737 va_copy(cap,args);
738 n = vsnprintf (p, size, format, cap);
739 va_end(cap);
740 #else
741 /*this works on 32 bits, luckily*/
742 n = vsnprintf (p, size, format, args);
743 #endif
744 /* If that worked, return the string. */
745 if (n > -1 && n < size)
746 return p;
747 //bc_tester_printf(bc_printf_verbosity_error, "Reallocing space.");
748 /* Else try again with more space. */
749 if (n > -1) /* glibc 2.1 */
750 size = n + 1; /* precisely what is needed */
751 else /* glibc 2.0 */
752 size *= 2; /* twice the old size */
753 if ((np = realloc (p, size)) == NULL)
754 {
755 free(p);
756 return NULL;
757 }
758 else
759 {
760 p = np;
761 }
762 }
763 }
764
bc_sprintf(const char * format,...)765 char* bc_sprintf(const char* format, ...) {
766 va_list args;
767 char* res;
768 va_start(args, format);
769 res = bc_sprintfva(format, args);
770 va_end (args);
771 return res;
772 }
773
bc_free(void * ptr)774 void bc_free(void *ptr) {
775 free(ptr);
776 }
777
bc_tester_current_suite_name(void)778 const char * bc_tester_current_suite_name(void) {
779 return bc_current_suite_name;
780 }
781
bc_tester_current_test_name(void)782 const char * bc_tester_current_test_name(void) {
783 return bc_current_test_name;
784 }
785
bc_tester_current_test_tags(void)786 const char ** bc_tester_current_test_tags(void) {
787 if (bc_current_suite_name && bc_current_test_name) {
788 int suite_index = bc_tester_suite_index(bc_current_suite_name);
789 int test_index = bc_tester_test_index(test_suite[suite_index], bc_current_test_name);
790 return test_suite[suite_index]->tests[test_index].tags;
791 }
792 return NULL;
793 }
794
bc_get_number_of_failures(void)795 unsigned int bc_get_number_of_failures(void) {
796 return CU_get_number_of_failures();
797 }
798
bc_set_trace_handler(void (* handler)(int,const char *,va_list))799 void bc_set_trace_handler(void(*handler)(int, const char*, va_list)) {
800 #ifdef HAVE_CU_SET_TRACE_HANDLER
801 CU_set_trace_handler(handler);
802 #else
803 bc_tester_printf(bc_printf_verbosity_error, "CU_set_trace_handler not implemented");
804 #endif
805 }
806
bc_assert(const char * file,int line,int predicate,const char * format)807 int bc_assert(const char* file, int line, int predicate, const char* format) {
808 if (!predicate) bc_tester_printf(bc_printf_verbosity_info, format, NULL);
809 return CU_assertImplementation(predicate, line, format, file, "", FALSE);
810 }
811