clar.c 14 KB
Newer Older
Russell Belfer committed
1 2 3 4 5 6
/*
 * Copyright (c) Vicent Marti. All rights reserved.
 *
 * This file is part of clar, distributed under the ISC license.
 * For full terms see the included COPYING file.
 */
Vicent Marti committed
7 8 9 10 11 12 13
#include <assert.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>
14
#include <wchar.h>
Vicent Marti committed
15 16 17 18 19 20 21 22 23 24 25 26 27

/* required for sandboxing */
#include <sys/types.h>
#include <sys/stat.h>

#ifdef _WIN32
#	include <windows.h>
#	include <io.h>
#	include <shellapi.h>
#	include <direct.h>

#	define _MAIN_CC __cdecl

Russell Belfer committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#	ifndef stat
#		define stat(path, st) _stat(path, st)
#	endif
#	ifndef mkdir
#		define mkdir(path, mode) _mkdir(path)
#	endif
#	ifndef chdir
#		define chdir(path) _chdir(path)
#	endif
#	ifndef access
#		define access(path, mode) _access(path, mode)
#	endif
#	ifndef strdup
#		define strdup(str) _strdup(str)
#	endif
#	ifndef strcasecmp
#		define strcasecmp(a,b) _stricmp(a,b)
#	endif
Vicent Marti committed
46 47 48

#	ifndef __MINGW32__
#		pragma comment(lib, "shell32")
Russell Belfer committed
49 50 51 52 53 54 55 56 57 58
#		ifndef strncpy
#			define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
#		endif
#		ifndef W_OK
#			define W_OK 02
#		endif
#		ifndef S_ISDIR
#			define S_ISDIR(x) ((x & _S_IFDIR) != 0)
#		endif
#		define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)
Vicent Marti committed
59
#	else
Russell Belfer committed
60 61 62 63 64 65 66 67
#		define p_snprintf snprintf
#	endif

#	ifndef PRIuZ
#		define PRIuZ "Iu"
#	endif
#	ifndef PRIxZ
#		define PRIxZ "Ix"
Vicent Marti committed
68
#	endif
Edward Thomson committed
69 70 71 72

#	ifdef _MSC_VER
	typedef struct stat STAT_T;
#	else
Vicent Marti committed
73
	typedef struct _stat STAT_T;
Edward Thomson committed
74
#	endif
Vicent Marti committed
75 76 77 78
#else
#	include <sys/wait.h> /* waitpid(2) */
#	include <unistd.h>
#	define _MAIN_CC
Russell Belfer committed
79 80 81 82 83 84 85
#	define p_snprintf snprintf
#	ifndef PRIuZ
#		define PRIuZ "zu"
#	endif
#	ifndef PRIxZ
#		define PRIxZ "zx"
#	endif
Vicent Marti committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	typedef struct stat STAT_T;
#endif

#include "clar.h"

static void fs_rm(const char *_source);
static void fs_copy(const char *_source, const char *dest);

static const char *
fixture_path(const char *base, const char *fixture_name);

struct clar_error {
	const char *test;
	int test_number;
	const char *suite;
	const char *file;
	int line_number;
	const char *error_msg;
	char *description;

	struct clar_error *next;
};

static struct {
Edward Thomson committed
110 111 112
	int argc;
	char **argv;

Vicent Marti committed
113
	enum cl_test_status test_status;
Vicent Marti committed
114 115 116
	const char *active_test;
	const char *active_suite;

Vicent Marti committed
117
	int total_skipped;
Vicent Marti committed
118 119 120 121 122 123 124
	int total_errors;

	int tests_ran;
	int suites_ran;

	int report_errors_only;
	int exit_on_error;
Russell Belfer committed
125
	int report_suite_names;
Vicent Marti committed
126 127 128 129 130 131 132 133 134

	struct clar_error *errors;
	struct clar_error *last_error;

	void (*local_cleanup)(void *);
	void *local_cleanup_payload;

	jmp_buf trampoline;
	int trampoline_enabled;
135 136 137 138

	cl_trace_cb *pfn_trace_cb;
	void *trace_payload;

Vicent Marti committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
} _clar;

struct clar_func {
	const char *name;
	void (*ptr)(void);
};

struct clar_suite {
	const char *name;
	struct clar_func initialize;
	struct clar_func cleanup;
	const struct clar_func *tests;
	size_t test_count;
	int enabled;
};

/* From clar_print_*.c */
static void clar_print_init(int test_count, int suite_count, const char *suite_names);
static void clar_print_shutdown(int test_count, int suite_count, int error_count);
static void clar_print_error(int num, const struct clar_error *error);
Vicent Marti committed
159
static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed);
Vicent Marti committed
160 161 162 163 164 165 166 167 168 169
static void clar_print_onsuite(const char *suite_name, int suite_index);
static void clar_print_onabort(const char *msg, ...);

/* From clar_sandbox.c */
static void clar_unsandbox(void);
static int clar_sandbox(void);

/* Load the declarations for the test suite */
#include "clar.suite"

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

#define CL_TRACE(ev)													\
	do {																\
		if (_clar.pfn_trace_cb)											\
			_clar.pfn_trace_cb(ev,										\
							   _clar.active_suite,						\
							   _clar.active_test,						\
							   _clar.trace_payload);					\
	} while (0)

void cl_trace_register(cl_trace_cb *cb, void *payload)
{
	_clar.pfn_trace_cb = cb;
	_clar.trace_payload = payload;
}


Vicent Marti committed
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
/* Core test functions */
static void
clar_report_errors(void)
{
	int i = 1;
	struct clar_error *error, *next;

	error = _clar.errors;
	while (error != NULL) {
		next = error->next;
		clar_print_error(i++, error);
		free(error->description);
		free(error);
		error = next;
	}

	_clar.errors = _clar.last_error = NULL;
}

static void
clar_run_test(
	const struct clar_func *test,
	const struct clar_func *initialize,
	const struct clar_func *cleanup)
{
Vicent Marti committed
212
	_clar.test_status = CL_TEST_OK;
Vicent Marti committed
213 214
	_clar.trampoline_enabled = 1;

215 216
	CL_TRACE(CL_TRACE__TEST__BEGIN);

Vicent Marti committed
217 218 219 220
	if (setjmp(_clar.trampoline) == 0) {
		if (initialize->ptr != NULL)
			initialize->ptr();

221
		CL_TRACE(CL_TRACE__TEST__RUN_BEGIN);
Vicent Marti committed
222
		test->ptr();
223
		CL_TRACE(CL_TRACE__TEST__RUN_END);
Vicent Marti committed
224 225 226 227 228 229 230 231 232 233
	}

	_clar.trampoline_enabled = 0;

	if (_clar.local_cleanup != NULL)
		_clar.local_cleanup(_clar.local_cleanup_payload);

	if (cleanup->ptr != NULL)
		cleanup->ptr();

234 235
	CL_TRACE(CL_TRACE__TEST__END);

Vicent Marti committed
236 237 238 239 240 241
	_clar.tests_ran++;

	/* remove any local-set cleanup methods */
	_clar.local_cleanup = NULL;
	_clar.local_cleanup_payload = NULL;

Vicent Marti committed
242
	if (_clar.report_errors_only) {
Vicent Marti committed
243
		clar_report_errors();
Vicent Marti committed
244 245 246
	} else {
		clar_print_ontest(test->name, _clar.tests_ran, _clar.test_status);
	}
Vicent Marti committed
247 248 249
}

static void
250
clar_run_suite(const struct clar_suite *suite, const char *filter)
Vicent Marti committed
251 252
{
	const struct clar_func *test = suite->tests;
253
	size_t i, matchlen;
Vicent Marti committed
254 255 256 257 258 259 260 261 262 263 264

	if (!suite->enabled)
		return;

	if (_clar.exit_on_error && _clar.total_errors)
		return;

	if (!_clar.report_errors_only)
		clar_print_onsuite(suite->name, ++_clar.suites_ran);

	_clar.active_suite = suite->name;
265 266
	_clar.active_test = NULL;
	CL_TRACE(CL_TRACE__SUITE_BEGIN);
Vicent Marti committed
267

268
	if (filter) {
269
		size_t suitelen = strlen(suite->name);
270 271 272
		matchlen = strlen(filter);
		if (matchlen <= suitelen) {
			filter = NULL;
273
		} else {
274 275 276 277
			filter += suitelen;
			while (*filter == ':')
				++filter;
			matchlen = strlen(filter);
278 279 280
		}
	}

Vicent Marti committed
281
	for (i = 0; i < suite->test_count; ++i) {
282
		if (filter && strncmp(test[i].name, filter, matchlen))
283 284
			continue;

Vicent Marti committed
285 286 287 288 289 290
		_clar.active_test = test[i].name;
		clar_run_test(&test[i], &suite->initialize, &suite->cleanup);

		if (_clar.exit_on_error && _clar.total_errors)
			return;
	}
291 292 293

	_clar.active_test = NULL;
	CL_TRACE(CL_TRACE__SUITE_END);
Vicent Marti committed
294 295 296 297 298 299 300
}

static void
clar_usage(const char *arg)
{
	printf("Usage: %s [options]\n\n", arg);
	printf("Options:\n");
301
	printf("  -sname\tRun only the suite with `name` (can go to individual test name)\n");
Russell Belfer committed
302 303
	printf("  -iname\tInclude the suite with `name`\n");
	printf("  -xname\tExclude the suite with `name`\n");
304
	printf("  -v    \tIncrease verbosity (show suite names)\n");
Russell Belfer committed
305 306 307
	printf("  -q    \tOnly report tests that had an error\n");
	printf("  -Q    \tQuit as soon as a test fails\n");
	printf("  -l    \tPrint suite names\n");
Vicent Marti committed
308 309 310 311 312 313 314 315
	exit(-1);
}

static void
clar_parse_args(int argc, char **argv)
{
	int i;

316
	/* Verify options before execute */
Vicent Marti committed
317 318 319
	for (i = 1; i < argc; ++i) {
		char *argument = argv[i];

320 321
		if (argument[0] != '-' || argument[1] == '\0'
		    || strchr("sixvqQl", argument[1]) == NULL) {
Vicent Marti committed
322
			clar_usage(argv[0]);
323 324 325 326 327
		}
	}

	for (i = 1; i < argc; ++i) {
		char *argument = argv[i];
Vicent Marti committed
328 329 330 331 332

		switch (argument[1]) {
		case 's':
		case 'i':
		case 'x': { /* given suite name */
Russell Belfer committed
333
			int offset = (argument[2] == '=') ? 3 : 2, found = 0;
Vicent Marti committed
334
			char action = argument[1];
335
			size_t j, arglen, suitelen, cmplen;
Vicent Marti committed
336 337

			argument += offset;
338
			arglen = strlen(argument);
Vicent Marti committed
339

340
			if (arglen == 0)
Vicent Marti committed
341 342 343
				clar_usage(argv[0]);

			for (j = 0; j < _clar_suite_count; ++j) {
344 345
				suitelen = strlen(_clar_suites[j].name);
				cmplen = (arglen < suitelen) ? arglen : suitelen;
346 347

				if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
348
					int exact = (arglen >= suitelen);
Russell Belfer committed
349

350 351 352 353 354 355
					/* Do we have a real suite prefix separated by a
					 * trailing '::' or just a matching substring? */
					if (arglen > suitelen && (argument[suitelen] != ':'
						    || argument[suitelen + 1] != ':'))
					    continue;

Russell Belfer committed
356 357 358 359 360
					++found;

					if (!exact)
						_clar.report_suite_names = 1;

Vicent Marti committed
361
					switch (action) {
362
					case 's': _clar_suites[j].enabled = 1; clar_run_suite(&_clar_suites[j], argument); break;
363 364
					case 'i': _clar_suites[j].enabled = 1; break;
					case 'x': _clar_suites[j].enabled = 0; break;
Vicent Marti committed
365
					}
Russell Belfer committed
366 367 368

					if (exact)
						break;
Vicent Marti committed
369 370 371
				}
			}

Russell Belfer committed
372
			if (!found) {
Vicent Marti committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
				clar_print_onabort("No suite matching '%s' found.\n", argument);
				exit(-1);
			}
			break;
		}

		case 'q':
			_clar.report_errors_only = 1;
			break;

		case 'Q':
			_clar.exit_on_error = 1;
			break;

		case 'l': {
			size_t j;
			printf("Test suites (use -s<name> to run just one):\n");
			for (j = 0; j < _clar_suite_count; ++j)
				printf(" %3d: %s\n", (int)j, _clar_suites[j].name);

			exit(0);
		}

396 397 398 399
		case 'v':
			_clar.report_suite_names = 1;
			break;

Vicent Marti committed
400
		default:
401
			assert(!"Unexpected commandline argument!");
Vicent Marti committed
402 403 404 405
		}
	}
}

Edward Thomson committed
406 407
void
clar_test_init(int argc, char **argv)
Vicent Marti committed
408 409 410 411 412 413 414 415 416 417 418 419
{
	clar_print_init(
		(int)_clar_callback_count,
		(int)_clar_suite_count,
		""
	);

	if (clar_sandbox() < 0) {
		clar_print_onabort("Failed to sandbox the test runner.\n");
		exit(-1);
	}

Edward Thomson committed
420 421
	_clar.argc = argc;
	_clar.argv = argv;
Edward Thomson committed
422
}
Vicent Marti committed
423

Edward Thomson committed
424
int
425
clar_test_run(void)
Edward Thomson committed
426
{
Edward Thomson committed
427 428 429
	if (_clar.argc > 1)
		clar_parse_args(_clar.argc, _clar.argv);

Vicent Marti committed
430 431 432
	if (!_clar.suites_ran) {
		size_t i;
		for (i = 0; i < _clar_suite_count; ++i)
433
			clar_run_suite(&_clar_suites[i], NULL);
Vicent Marti committed
434 435
	}

Edward Thomson committed
436 437 438 439
	return _clar.total_errors;
}

void
440
clar_test_shutdown(void)
Edward Thomson committed
441
{
Vicent Marti committed
442 443 444 445 446 447 448
	clar_print_shutdown(
		_clar.tests_ran,
		(int)_clar_suite_count,
		_clar.total_errors
	);

	clar_unsandbox();
Edward Thomson committed
449 450 451 452 453 454 455 456 457 458 459 460
}

int
clar_test(int argc, char **argv)
{
	int errors;

	clar_test_init(argc, argv);
	errors = clar_test_run();
	clar_test_shutdown();

	return errors;
Vicent Marti committed
461 462
}

Vicent Marti committed
463 464 465 466 467 468 469 470 471
static void abort_test(void)
{
	if (!_clar.trampoline_enabled) {
		clar_print_onabort(
				"Fatal error: a cleanup method raised an exception.");
		clar_report_errors();
		exit(-1);
	}

472
	CL_TRACE(CL_TRACE__TEST__LONGJMP);
Vicent Marti committed
473 474 475 476 477 478 479 480 481 482
	longjmp(_clar.trampoline, -1);
}

void clar__skip(void)
{
	_clar.test_status = CL_TEST_SKIP;
	_clar.total_skipped++;
	abort_test();
}

Russell Belfer committed
483
void clar__fail(
Vicent Marti committed
484 485 486 487 488 489
	const char *file,
	int line,
	const char *error_msg,
	const char *description,
	int should_abort)
{
Russell Belfer committed
490
	struct clar_error *error = calloc(1, sizeof(struct clar_error));
Vicent Marti committed
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510

	if (_clar.errors == NULL)
		_clar.errors = error;

	if (_clar.last_error != NULL)
		_clar.last_error->next = error;

	_clar.last_error = error;

	error->test = _clar.active_test;
	error->test_number = _clar.tests_ran;
	error->suite = _clar.active_suite;
	error->file = file;
	error->line_number = line;
	error->error_msg = error_msg;

	if (description != NULL)
		error->description = strdup(description);

	_clar.total_errors++;
Vicent Marti committed
511
	_clar.test_status = CL_TEST_FAILURE;
Vicent Marti committed
512

Vicent Marti committed
513 514
	if (should_abort)
		abort_test();
Vicent Marti committed
515 516
}

Russell Belfer committed
517 518 519 520 521 522 523 524 525 526 527 528 529 530
void clar__assert(
	int condition,
	const char *file,
	int line,
	const char *error_msg,
	const char *description,
	int should_abort)
{
	if (condition)
		return;

	clar__fail(file, line, error_msg, description, should_abort);
}

Russell Belfer committed
531
void clar__assert_equal(
Vicent Marti committed
532 533 534
	const char *file,
	int line,
	const char *err,
Russell Belfer committed
535 536 537
	int should_abort,
	const char *fmt,
	...)
Vicent Marti committed
538
{
Russell Belfer committed
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
	va_list args;
	char buf[4096];
	int is_equal = 1;

	va_start(args, fmt);

	if (!strcmp("%s", fmt)) {
		const char *s1 = va_arg(args, const char *);
		const char *s2 = va_arg(args, const char *);
		is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2);

		if (!is_equal) {
			if (s1 && s2) {
				int pos;
				for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
					/* find differing byte offset */;
				p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
					s1, s2, pos);
			} else {
				p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
			}
		}
	}
Edward Thomson committed
562 563 564
	else if(!strcmp("%.*s", fmt)) {
		const char *s1 = va_arg(args, const char *);
		const char *s2 = va_arg(args, const char *);
Edward Thomson committed
565
		int len = va_arg(args, int);
Edward Thomson committed
566 567 568 569
		is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp(s1, s2, len);

		if (!is_equal) {
			if (s1 && s2) {
Edward Thomson committed
570
				int pos;
Edward Thomson committed
571 572 573 574 575 576 577 578 579
				for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos)
					/* find differing byte offset */;
				p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)",
					len, s1, len, s2, pos);
			} else {
				p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2);
			}
		}
	}
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
	else if (!strcmp("%ls", fmt)) {
		const wchar_t *wcs1 = va_arg(args, const wchar_t *);
		const wchar_t *wcs2 = va_arg(args, const wchar_t *);
		is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1, wcs2);

		if (!is_equal) {
			if (wcs1 && wcs2) {
				int pos;
				for (pos = 0; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos)
					/* find differing byte offset */;
				p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
					wcs1, wcs2, pos);
			} else {
				p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
			}
		}
	}
	else if(!strcmp("%.*ls", fmt)) {
		const wchar_t *wcs1 = va_arg(args, const wchar_t *);
		const wchar_t *wcs2 = va_arg(args, const wchar_t *);
		int len = va_arg(args, int);
		is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1, wcs2, len);

		if (!is_equal) {
			if (wcs1 && wcs2) {
				int pos;
				for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
					/* find differing byte offset */;
				p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
					len, wcs1, len, wcs2, pos);
			} else {
				p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
			}
		}
	}
615
	else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
Russell Belfer committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
		size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
		is_equal = (sz1 == sz2);
		if (!is_equal) {
			int offset = p_snprintf(buf, sizeof(buf), fmt, sz1);
			strncat(buf, " != ", sizeof(buf) - offset);
			p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, sz2);
		}
	}
	else if (!strcmp("%p", fmt)) {
		void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
		is_equal = (p1 == p2);
		if (!is_equal)
			p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
	}
	else {
		int i1 = va_arg(args, int), i2 = va_arg(args, int);
		is_equal = (i1 == i2);
		if (!is_equal) {
			int offset = p_snprintf(buf, sizeof(buf), fmt, i1);
			strncat(buf, " != ", sizeof(buf) - offset);
			p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, i2);
637
		}
Vicent Marti committed
638 639
	}

Russell Belfer committed
640 641 642
	va_end(args);

	if (!is_equal)
Russell Belfer committed
643
		clar__fail(file, line, err, buf, should_abort);
Vicent Marti committed
644 645 646 647 648 649 650 651 652 653 654 655
}

void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
{
	_clar.local_cleanup = cleanup;
	_clar.local_cleanup_payload = opaque;
}

#include "clar/sandbox.h"
#include "clar/fixtures.h"
#include "clar/fs.h"
#include "clar/print.h"