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