bc_tester_utils.c 21.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
tester - liblinphone test suite
Copyright (C) 2013  Belledonne Communications SARL

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


20 21
/* this must be provided at compile time*/
#include BC_CONFIG_FILE
22

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
23
#include "bc_tester_utils.h"
24 25

#include <stdlib.h>
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
26
#include <time.h>
27
#include <stdio.h>
28

29 30 31 32 33
#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4)
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wstrict-prototypes"

34
#include "CUnit/Basic.h"
35
#include "CUnit/Automated.h"
Ghislain MARY's avatar
Ghislain MARY committed
36
#include "CUnit/MyMem.h"
37

38 39 40 41
#if __clang__ || ((__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4)
#pragma GCC diagnostic pop
#endif

42 43 44 45 46 47
#ifdef _WIN32
#if defined(__MINGW32__) || !defined(WINAPI_FAMILY_PARTITION) || !defined(WINAPI_PARTITION_DESKTOP)
#define BC_TESTER_WINDOWS_DESKTOP 1
#elif defined(WINAPI_FAMILY_PARTITION)
#if defined(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#define BC_TESTER_WINDOWS_DESKTOP 1
48
#endif
49 50
#if defined(WINAPI_PARTITION_PHONE_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
#define BC_TESTER_WINDOWS_PHONE 1
51
#endif
52 53 54 55 56 57
#if defined(WINAPI_PARTITION_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#define BC_TESTER_WINDOWS_UNIVERSAL 1
#endif
#endif
#endif

58 59 60 61
#ifdef __linux
/*for monitoring total space allocated via malloc*/
#include <malloc.h>
#endif
62 63

static char *bc_tester_resource_dir_prefix = NULL;
64
// by default writable will always write near the executable
65
static char *bc_tester_writable_dir_prefix = NULL;
66

67 68 69
static char *bc_current_suite_name = NULL;
static char *bc_current_test_name = NULL;

70 71 72
int bc_printf_verbosity_info;
int bc_printf_verbosity_error;

73 74 75
static test_suite_t **test_suite = NULL;
static int nb_test_suites = 0;

76 77
#ifdef HAVE_CU_CURSES
#include "CUnit/CUCurses.h"
78 79 80
static unsigned char curses = 0;
#endif

81
char* xml_file = "CUnitAutomated-Results.xml";
82
int   xml_enabled = 0;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
83 84 85
char * suite_name = NULL;
char * test_name = NULL;
char * tag_name = NULL;
86 87
static long max_vm_kb = 0;

88
void (*tester_printf_va)(int level, const char *format, va_list args);
89

90
void bc_tester_printf(int level, const char *format, ...) {
91
	va_list args;
92 93
	va_start (args, format);
	tester_printf_va(level, format, args);
94 95 96
	va_end (args);
}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
97
int bc_tester_run_suite(test_suite_t *suite, const char *tag_name) {
98
	int i;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	CU_pSuite pSuite;

	if (tag_name != NULL) {
		int j;
		int nb_tests_for_tag = 0;
		for (i = 0; i < suite->nb_tests; i++) {
			for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) {
				if ((suite->tests[i].tags[j] != NULL) && (strcasecmp(tag_name, suite->tests[i].tags[j]) == 0)) {
					nb_tests_for_tag++;
				}
			}
		}
		if (nb_tests_for_tag > 0) {
			pSuite = CU_add_suite(suite->name, suite->before_all, suite->after_all);
			for (i = 0; i < suite->nb_tests; i++) {
				for (j = 0; j < (sizeof(suite->tests[i].tags) / sizeof(suite->tests[i].tags[0])); j++) {
					if ((suite->tests[i].tags[j] != NULL) && (strcasecmp(tag_name, suite->tests[i].tags[j]) == 0)) {
						if (NULL == CU_add_test(pSuite, suite->tests[i].name, suite->tests[i].func)) {
							return CU_get_error();
						}
					}
				}
			}
		}
	} else {
		pSuite = CU_add_suite(suite->name, suite->before_all, suite->after_all);
		for (i = 0; i < suite->nb_tests; i++) {
			if (NULL == CU_add_test(pSuite, suite->tests[i].name, suite->tests[i].func)) {
				return CU_get_error();
			}
129 130 131
		}
	}

132
	return 0;
133 134
}

135
const char * bc_tester_suite_name(int suite_index) {
136 137
	if (suite_index >= nb_test_suites) return NULL;
	return test_suite[suite_index]->name;
138 139
}

140
int bc_tester_suite_index(const char *suite_name) {
141 142
	int i;

143
	for (i = 0; i < nb_test_suites; i++) {
144
		if (strcmp(suite_name, test_suite[i]->name) == 0) {
145 146 147 148 149 150
			return i;
		}
	}

	return -1;
}
151

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
152 153 154 155 156 157 158 159 160 161 162 163 164

int bc_tester_test_index(test_suite_t *suite, const char *test_name) {
	int i;

	for (i = 0; i < suite->nb_tests; i++) {
		if (strcmp(test_name, suite->tests[i].name) == 0) {
			return i;
		}
	}

	return -1;
}

165
int bc_tester_nb_suites(void) {
166 167 168 169 170
	return nb_test_suites;
}

const char * bc_tester_test_name(const char *suite_name, int test_index) {
	int suite_index = bc_tester_suite_index(suite_name);
171 172 173
	if ((suite_index < 0) || (suite_index >= nb_test_suites)) return NULL;
	if (test_index >= test_suite[suite_index]->nb_tests) return NULL;
	return test_suite[suite_index]->tests[test_index].name;
174 175
}

176 177
int bc_tester_nb_tests(const char *suite_name) {
	int i = bc_tester_suite_index(suite_name);
178 179 180 181
	if (i < 0) return 0;
	return test_suite[i]->nb_tests;
}

182
void bc_tester_list_suites(void) {
183 184
	int j;
	for(j=0;j<nb_test_suites;j++) {
185
		bc_tester_printf(bc_printf_verbosity_info, "%s", bc_tester_suite_name(j));
186
	}
187 188
}

189
void bc_tester_list_tests(const char *suite_name) {
190
	int j;
191 192
	for( j = 0; j < bc_tester_nb_tests(suite_name); j++) {
		const char *test_name = bc_tester_test_name(suite_name, j);
193
		bc_tester_printf(bc_printf_verbosity_info, "%s", test_name);
194
	}
195 196 197 198 199
}

static void all_complete_message_handler(const CU_pFailureRecord pFailure) {
#ifdef HAVE_CU_GET_SUITE
	char * results = CU_get_run_results_string();
200
	bc_tester_printf(bc_printf_verbosity_info,"\n%s",results);
Ghislain MARY's avatar
Ghislain MARY committed
201
	CU_FREE(results);
202 203 204 205
#endif
}

static void suite_init_failure_message_handler(const CU_pSuite pSuite) {
206
	bc_tester_printf(bc_printf_verbosity_error,"Suite initialization failed for [%s]", pSuite->pName);
207 208 209
}

static void suite_cleanup_failure_message_handler(const CU_pSuite pSuite) {
210
	bc_tester_printf(bc_printf_verbosity_error,"Suite cleanup failed for [%s]", pSuite->pName);
211 212 213
}

#ifdef HAVE_CU_GET_SUITE
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
214
static time_t suite_start_time = 0;
215
static void suite_start_message_handler(const CU_pSuite pSuite) {
216
	bc_tester_printf(bc_printf_verbosity_info,"Suite [%s] started\n", pSuite->pName);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
217
	suite_start_time = time(NULL);
218
	bc_current_suite_name = pSuite->pName;
219 220
}
static void suite_complete_message_handler(const CU_pSuite pSuite, const CU_pFailureRecord pFailure) {
221 222
	bc_tester_printf(bc_printf_verbosity_info, "Suite [%s] ended in %lu sec\n", pSuite->pName,
					 time(NULL) - suite_start_time);
223 224
}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
225
static time_t test_start_time = 0;
226
static void test_start_message_handler(const CU_pTest pTest, const CU_pSuite pSuite) {
227 228 229 230
	int suite_index = bc_tester_suite_index(pSuite->pName);
	if (test_suite[suite_index]->before_each) {
		test_suite[suite_index]->before_each();
	}
231 232
	bc_tester_printf(bc_printf_verbosity_info,"Suite [%s] Test [%s] started", pSuite->pName,pTest->pName);
	test_start_time = time(NULL);
233
	bc_current_test_name = pTest->pName;
234 235 236
}

/*derivated from cunit*/
237 238
static void test_complete_message_handler(const CU_pTest pTest, const CU_pSuite pSuite,
										  const CU_pFailureRecord pFailureList) {
239
	int i;
240
	int suite_index = bc_tester_suite_index(pSuite->pName);
241
	CU_pFailureRecord pFailure = pFailureList;
242 243
	char *buffer = NULL;
	char* result = bc_sprintf("Suite [%s] Test [%s] %s in %lu secs", pSuite->pName, pTest->pName,
244
			 pFailure ? "failed" : "passed", (unsigned long)(time(NULL) - test_start_time));
245

246
	if (pFailure) {
247 248 249 250 251 252 253 254 255
		for (i = 1; (NULL != pFailure); pFailure = pFailure->pNext, i++) {
			buffer = bc_sprintf("%s\n    %d. %s:%u  - %s",
									result,
									i,
									(NULL != pFailure->strFileName) ? pFailure->strFileName : "",
									pFailure->uiLineNumber,
									(NULL != pFailure->strCondition) ? pFailure->strCondition : "");
			free(result);
			result = buffer;
256 257
		}
	}
258

259
	bc_tester_printf(bc_printf_verbosity_info,"%s", result);
260
	free(result);
261

262
	if (test_suite[suite_index]->after_each) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
263 264 265 266 267
		int err = test_suite[suite_index]->after_each();
		//if test passed but not after_each, count it as failure
		if (err && !pFailure) {
			CU_get_run_summary()->nTestsFailed++;
		}
268 269 270 271
	}
	//insert empty line
	bc_tester_printf(bc_printf_verbosity_info,"");

272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
#ifdef __linux
	/* use mallinfo() to monitor allocated space. It is linux specific but other methods don't work:
	 * setrlimit() RLIMIT_DATA doesn't count memory allocated via mmap() (which is used internally by malloc)
	 * setrlimit() RLIMIT_AS works but also counts virtual memory allocated by thread stacks, which is very big and
	 * hardly controllable.
	 * setrlimit() RLIMIT_RSS does nothing interesting on linux.
	 * getrusage() of RSS is unreliable: memory blocks can be leaked without being read or written, which would not
	 * appear in RSS.
	 * mallinfo() itself is the less worse solution. Allocated bytes are returned as 'int' so limited to 2GB
	 */
	if (max_vm_kb) {
		struct mallinfo minfo = mallinfo();
		if (minfo.uordblks > max_vm_kb * 1024) {
			bc_tester_printf(
				bc_printf_verbosity_error,
				"The program exceeded the maximum amount of memory allocatable (%i bytes), aborting now.\n",
				minfo.uordblks);
			abort();
		}
	}
#endif
293 294 295
}
#endif

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
296
int bc_tester_run_tests(const char *suite_name, const char *test_name, const char *tag_name) {
297
	int i;
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
298

299
	/* initialize the CUnit test registry */
300 301 302
	if (CUE_SUCCESS != CU_initialize_registry())
		return CU_get_error();

303
	for (i = 0; i < nb_test_suites; i++) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
304
		bc_tester_run_suite(test_suite[i], tag_name);
305 306 307 308 309 310 311 312 313 314 315
	}
#ifdef HAVE_CU_GET_SUITE
	CU_set_suite_start_handler(suite_start_message_handler);
	CU_set_suite_complete_handler(suite_complete_message_handler);
	CU_set_test_start_handler(test_start_message_handler);
	CU_set_test_complete_handler(test_complete_message_handler);
#endif
	CU_set_all_test_complete_handler(all_complete_message_handler);
	CU_set_suite_init_failure_handler(suite_init_failure_message_handler);
	CU_set_suite_cleanup_failure_handler(suite_cleanup_failure_message_handler);

316
	if( xml_enabled != 0 ){
317 318 319
		CU_automated_run_tests();
	} else {

320
#ifndef HAVE_CU_GET_SUITE
321
		if( suite_name ){
322
			bc_tester_printf(bc_printf_verbosity_info, "Tester compiled without CU_get_suite() function, running all tests instead of suite '%s'", suite_name);
323 324 325 326 327 328
		}
#else
		if (suite_name){
			CU_pSuite suite;
			suite=CU_get_suite(suite_name);
			if (!suite) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
329 330 331 332 333
				if (tag_name != NULL) {
					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);
				} else {
					bc_tester_printf(bc_printf_verbosity_error, "Could not find suite '%s'. Available suites are:", suite_name);
				}
334
				bc_tester_list_suites();
335 336 337 338
				return -1;
			} else if (test_name) {
				CU_pTest test=CU_get_test_by_name(test_name, suite);
				if (!test) {
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
339 340 341 342 343
					if (tag_name != NULL) {
						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);
					} else {
						bc_tester_printf(bc_printf_verbosity_error, "Could not find test '%s' in suite '%s'. Available tests are:", test_name, suite_name);
					}
344
					// do not use suite_name here, since this method is case sensitive
345
					bc_tester_list_tests(suite->pName);
346 347 348
					return -2;
				} else {
					CU_ErrorCode err= CU_run_test(suite, test);
349
					if (err != CUE_SUCCESS) bc_tester_printf(bc_printf_verbosity_error, "CU_basic_run_test error %d", err);
350 351 352 353 354 355 356 357
				}
			} else {
				CU_run_suite(suite);
			}
		}
		else
#endif
		{
358
#ifdef HAVE_CU_CURSES
359 360 361 362 363 364 365
			if (curses) {
			/* Run tests using the CUnit curses interface */
				CU_curses_run_tests();
			}
			else
#endif
			{
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
366
				/* Run all tests using the CUnit Basic interface */
367 368 369 370
				CU_run_all_tests();
			}
		}
	}
371 372 373 374 375
#ifdef __linux
	bc_tester_printf(bc_printf_verbosity_info, "Still %i kilobytes allocated when all tests are finished.",
					 mallinfo().uordblks / 1024);
#endif

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
376
	return CU_get_number_of_tests_failed()!=0;
377 378 379

}

380 381

void bc_tester_helper(const char *name, const char* additionnal_helper) {
382 383
	bc_tester_printf(bc_printf_verbosity_info,
					 "%s --help\n"
384 385 386
#ifdef HAVE_CU_CURSES
					 "\t\t\t--curses\n"
#endif
387 388 389 390
					 "\t\t\t--list-suites\n"
					 "\t\t\t--list-tests <suite>\n"
					 "\t\t\t--suite <suite name>\n"
					 "\t\t\t--test <test name>\n"
391 392
					 "\t\t\t--resource-dir <folder path> (directory where tester resource are located)\n"
					 "\t\t\t--writable-dir <folder path> (directory where temporary files should be created)\n"
393 394 395 396 397
					 "\t\t\t--xml\n"
					 "\t\t\t--xml-file <xml file name>\n"
					 "\t\t\t--max-alloc <size in ko> (maximum ammount of memory obtained via malloc allocator)\n"
					 "And additionally:\n"
					 "%s",
398 399
					 name,
					 additionnal_helper);
400 401
}

402
#if !defined(BC_TESTER_WINDOWS_PHONE) && !defined(BC_TESTER_WINDOWS_UNIVERSAL) && !defined(__QNX__) && !defined(ANDROID) && !defined(IOS)
403
static int file_exists(const char* root_path) {
404 405 406
	char * res_path = bc_sprintf("%s/common/bc_completion", root_path);
	FILE* file = fopen(res_path, "r");
	int found = (file != NULL);
407
	free(res_path);
408 409 410
	if (file) {
		fclose(file);
	}
411 412
	return found;
}
413
#endif
414 415

static void detect_res_prefix(const char* prog) {
Ghislain MARY's avatar
Ghislain MARY committed
416
	char* progpath = NULL;
417
	FILE* writable_file = NULL;
418

419 420 421 422 423 424 425
	if (prog != NULL) {
		progpath = strdup(prog);
		if (strchr(prog, '/') != NULL) {
			progpath[strrchr(prog, '/') - prog + 1] = '\0';
		} else if (strchr(prog, '\\') != NULL) {
			progpath[strrchr(prog, '\\') - prog + 1] = '\0';
		}
426
	}
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
#if !defined(BC_TESTER_WINDOWS_PHONE) && !defined(BC_TESTER_WINDOWS_UNIVERSAL) && !defined(__QNX__) && !defined(ANDROID) && !defined(IOS)
	{
		char* prefix = NULL;

		if (file_exists(".")) {
			prefix = strdup(".");
		} else if (file_exists("..")) {
			prefix = strdup("..");
		} else if (progpath) {
			//for autotools, binary is in .libs/ subdirectory
			char * progpath2 = bc_sprintf("%s/../", progpath);
			if (file_exists(progpath)) {
				prefix = strdup(progpath);
			} else if (file_exists(progpath2)) {
				prefix = strdup(progpath2);
			}
			free(progpath2);
444
		}
445

446 447 448 449
		if (bc_tester_resource_dir_prefix != NULL && !file_exists(bc_tester_resource_dir_prefix)) {
			bc_tester_printf(bc_printf_verbosity_error, "Invalid provided resource directory: could not find expected resources in %s.\n", bc_tester_resource_dir_prefix);
			free(bc_tester_resource_dir_prefix);
			bc_tester_resource_dir_prefix = NULL;
450
		}
451

452 453 454 455 456 457 458 459 460 461 462
		if (prefix != NULL) {
			if (bc_tester_resource_dir_prefix == NULL) {
				bc_tester_printf(bc_printf_verbosity_error, "Resource directory set to %s\n", prefix);
				bc_tester_set_resource_dir_prefix(prefix);
			}

			if (bc_tester_writable_dir_prefix == NULL) {
				bc_tester_printf(bc_printf_verbosity_error, "Writable directory set to %s\n", prefix);
				bc_tester_set_writable_dir_prefix(prefix);
			}
			free(prefix);
463
		}
464
	}
465
#endif
466 467 468

	// check that we can write in writable directory
	if (bc_tester_writable_dir_prefix != NULL) {
Ghislain MARY's avatar
Ghislain MARY committed
469 470
		char * writable_file_path = bc_sprintf("%s/%s", bc_tester_writable_dir_prefix, ".bc_tester_utils.tmp");
		writable_file = fopen(writable_file_path, "w");
471 472 473
		if (writable_file) {
			fclose(writable_file);
		}
Ghislain MARY's avatar
Ghislain MARY committed
474
		free(writable_file_path);
475 476 477
	}
	if (bc_tester_resource_dir_prefix == NULL || writable_file == NULL) {
		if (bc_tester_resource_dir_prefix == NULL) {
478 479
			bc_tester_printf(bc_printf_verbosity_error, "Failed to detect resources for %s.\n", prog);
			bc_tester_printf(bc_printf_verbosity_error, "Could not find resource directory in %s! Please try again using option --resource-dir.\n", progpath);
480 481
		}
		if (writable_file == NULL) {
482
			bc_tester_printf(bc_printf_verbosity_error, "Failed to write file in %s. Please try again using option --writable-dir.\n", bc_tester_writable_dir_prefix);
483
		}
484
		abort();
485
	}
486 487 488 489

	if (progpath != NULL) {
		free(progpath);
	}
490 491
}

492
void bc_tester_init(void (*ftester_printf)(int level, const char *format, va_list args), int iverbosity_info, int iverbosity_error) {
493
	tester_printf_va = ftester_printf;
494 495
	bc_printf_verbosity_error = iverbosity_error;
	bc_printf_verbosity_info = iverbosity_info;
496
	bc_tester_writable_dir_prefix = strdup(".");
497 498
}

499
void bc_tester_set_max_vm(long amax_vm_kb) {
500
#ifdef __linux
501
	max_vm_kb = amax_vm_kb;
502 503 504 505 506 507
	bc_tester_printf(bc_printf_verbosity_info, "Maximum virtual memory space set to %li kilo bytes", max_vm_kb);
#else
	bc_tester_printf(bc_printf_verbosity_error, "Maximum virtual memory space setting is only implemented on Linux.");
#endif
}

508 509 510
int bc_tester_parse_args(int argc, char **argv, int argid)
{
	int i = argid;
511

512 513 514 515 516
	if (strcmp(argv[i],"--help")==0){
		return -1;
	} else if (strcmp(argv[i],"--test")==0){
		CHECK_ARG("--test", ++i, argc);
		test_name=argv[i];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
517
	} else if (strcmp(argv[i],"--suite")==0){
518 519
		CHECK_ARG("--suite", ++i, argc);
		suite_name=argv[i];
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
520 521 522
	} else if (strcmp(argv[i], "--tag") == 0) {
		CHECK_ARG("--tag", ++i, argc);
		tag_name = argv[i];
523
	} else if (strcmp(argv[i],"--list-suites")==0){
524
		bc_tester_list_suites();
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
525
		return 0;
526 527 528
	} else if (strcmp(argv[i],"--list-tests")==0){
		CHECK_ARG("--list-tests", ++i, argc);
		suite_name = argv[i];
529
		bc_tester_list_tests(suite_name);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
530
		return 0;
531 532 533 534 535 536
	} else if (strcmp(argv[i], "--xml-file") == 0){
		CHECK_ARG("--xml-file", ++i, argc);
		xml_file = argv[i];
		xml_enabled = 1;
	} else if (strcmp(argv[i], "--xml") == 0){
		xml_enabled = 1;
537 538 539
	} else if (strcmp(argv[i], "--max-alloc") == 0) {
		CHECK_ARG("--max-alloc", ++i, argc);
		max_vm_kb = atol(argv[i]);
540 541 542 543 544 545
	} else if (strcmp(argv[i], "--resource-dir") == 0) {
		CHECK_ARG("--resource-dir", ++i, argc);
		bc_tester_resource_dir_prefix = strdup(argv[i]);
	} else if (strcmp(argv[i], "--writable-dir") == 0) {
		CHECK_ARG("--writable-dir", ++i, argc);
		bc_tester_writable_dir_prefix = strdup(argv[i]);
546
	} else {
547
		bc_tester_printf(bc_printf_verbosity_error, "Unknown option \"%s\"\n", argv[i]);
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
548
		return -1;
549 550 551
	}

	if( xml_enabled && (suite_name || test_name) ){
552
		bc_tester_printf(bc_printf_verbosity_error, "Cannot use both XML and specific test suite\n");
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
553
		return -1;
554 555
	}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
556 557
	/* returns number of arguments read + 1 */
	return i - argid + 1;
558 559
}

560
int bc_tester_start(const char* prog_name) {
561
	int ret;
562

563 564
	detect_res_prefix(prog_name);

565 566 567
	if (max_vm_kb)
		bc_tester_set_max_vm(max_vm_kb);

568
	if( xml_enabled ){
569
		char * xml_tmp_file = bc_sprintf("%s.tmp", xml_file);
570 571 572 573
		CU_set_output_filename(xml_tmp_file);
		free(xml_tmp_file);
	}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
574
	ret = bc_tester_run_tests(suite_name, test_name, tag_name);
575 576 577

	return ret;
}
578
void bc_tester_add_suite(test_suite_t *suite) {
579 580 581 582 583 584 585 586 587 588
	if (test_suite == NULL) {
		test_suite = (test_suite_t **)malloc(10 * sizeof(test_suite_t *));
	}
	test_suite[nb_test_suites] = suite;
	nb_test_suites++;
	if ((nb_test_suites % 10) == 0) {
		test_suite = (test_suite_t **)realloc(test_suite, (nb_test_suites + 10) * sizeof(test_suite_t *));
	}
}

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
589
void bc_tester_uninit(void) {
Ghislain MARY's avatar
Ghislain MARY committed
590 591 592
	/* Redisplay list of failed tests on end */
	/*BUG: do not display list of failures on mingw, it crashes mysteriously*/
#if !defined(WIN32) && !defined(_MSC_VER)
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
593 594 595 596
	/* Redisplay list of failed tests on end */
	if (CU_get_number_of_failure_records()){
		CU_basic_show_failures(CU_get_failure_list());
	}
Ghislain MARY's avatar
Ghislain MARY committed
597
#endif
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
598
	CU_cleanup_registry();
599
	/*add missing final newline*/
600
	bc_tester_printf(bc_printf_verbosity_info,"");
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
601

602
	if( xml_enabled ){
603
		/*create real xml file only if tester did not crash*/
604
		char * xml_tmp_file = bc_sprintf("%s.tmp-Results.xml", xml_file);
605
		rename(xml_tmp_file, xml_file);
606
		free(xml_tmp_file);
607 608
	}

609 610 611 612 613
	if (test_suite != NULL) {
		free(test_suite);
		test_suite = NULL;
		nb_test_suites = 0;
	}
614 615 616 617 618 619 620 621 622

	if (bc_tester_resource_dir_prefix != NULL) {
		free(bc_tester_resource_dir_prefix);
		bc_tester_resource_dir_prefix = NULL;
	}
	if (bc_tester_writable_dir_prefix != NULL) {
		free(bc_tester_writable_dir_prefix);
		bc_tester_writable_dir_prefix = NULL;
	}
623
}
624

625 626
static void bc_tester_set_dir_prefix(char **prefix, const char *name) {
	if (*prefix != NULL) free(*prefix);
627
	*prefix = strdup(name);
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
}

const char * bc_tester_get_resource_dir_prefix(void) {
	return bc_tester_resource_dir_prefix;
}

void bc_tester_set_resource_dir_prefix(const char *name) {
	bc_tester_set_dir_prefix(&bc_tester_resource_dir_prefix, name);
}

const char * bc_tester_get_writable_dir_prefix(void) {
	return bc_tester_writable_dir_prefix;
}

void bc_tester_set_writable_dir_prefix(const char *name) {
	bc_tester_set_dir_prefix(&bc_tester_writable_dir_prefix, name);
}

static char * bc_tester_path(const char *prefix, const char *name) {
647
	if (name) {
648 649 650
		return bc_sprintf("%s/%s", prefix, name);
	} else {
		return NULL;
651 652
	}
}
653 654 655 656 657 658 659 660

char * bc_tester_res(const char *name) {
	return bc_tester_path(bc_tester_resource_dir_prefix, name);
}

char * bc_tester_file(const char *name) {
	return bc_tester_path(bc_tester_writable_dir_prefix, name);
}
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684

char* bc_sprintfva(const char* format, va_list args) {
	/* Guess we need no more than 100 bytes. */
	int n, size = 200;
	char *p,*np;
#ifndef WIN32
	va_list cap;/*copy of our argument list: a va_list cannot be re-used (SIGSEGV on linux 64 bits)*/
#endif
	if ((p = malloc(size)) == NULL)
		return NULL;
	while (1)
	{
		/* Try to print in the allocated space. */
#ifndef WIN32
		va_copy(cap,args);
		n = vsnprintf (p, size, format, cap);
		va_end(cap);
#else
		/*this works on 32 bits, luckily*/
		n = vsnprintf (p, size, format, args);
#endif
		/* If that worked, return the string. */
		if (n > -1 && n < size)
			return p;
685
		//bc_tester_printf(bc_printf_verbosity_error, "Reallocing space.\n");
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
		/* Else try again with more space. */
		if (n > -1)	/* glibc 2.1 */
			size = n + 1;	/* precisely what is needed */
		else		/* glibc 2.0 */
			size *= 2;	/* twice the old size */
		if ((np = realloc (p, size)) == NULL)
		{
			free(p);
			return NULL;
		}
		else
		{
			p = np;
		}
	}
}

char* bc_sprintf(const char* format, ...) {
	va_list args;
	char* res;
	va_start(args, format);
	res = bc_sprintfva(format, args);
	va_end (args);
	return res;
}
711

Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
712 713 714 715
void bc_free(void *ptr) {
	free(ptr);
}

716
const char * bc_tester_current_suite_name(void) {
717 718 719
	return bc_current_suite_name;
}

720
const char * bc_tester_current_test_name(void) {
721 722
	return bc_current_test_name;
}
Gautier Pelloux-Prayer's avatar
Gautier Pelloux-Prayer committed
723 724 725 726 727 728 729 730 731

const char ** bc_tester_current_test_tags(void) {
	if (bc_current_suite_name && bc_current_test_name) {
		int suite_index = bc_tester_suite_index(bc_current_suite_name);
		int test_index = bc_tester_test_index(test_suite[suite_index], bc_current_test_name);
		return test_suite[suite_index]->tests[test_index].tags;
	}
	return NULL;
}