clar.c 13.6 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 316 317 318 319 320 321 322 323 324 325
	exit(-1);
}

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

	for (i = 1; i < argc; ++i) {
		char *argument = argv[i];

		if (argument[0] != '-')
			clar_usage(argv[0]);

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

			argument += offset;
331
			arglen = strlen(argument);
Vicent Marti committed
332

333
			if (arglen == 0)
Vicent Marti committed
334 335 336
				clar_usage(argv[0]);

			for (j = 0; j < _clar_suite_count; ++j) {
337 338
				suitelen = strlen(_clar_suites[j].name);
				cmplen = (arglen < suitelen) ? arglen : suitelen;
339 340

				if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
341
					int exact = (arglen >= suitelen);
Russell Belfer committed
342 343 344 345 346 347

					++found;

					if (!exact)
						_clar.report_suite_names = 1;

Vicent Marti committed
348
					switch (action) {
349
					case 's': _clar_suites[j].enabled = 1; clar_run_suite(&_clar_suites[j], argument); break;
350 351
					case 'i': _clar_suites[j].enabled = 1; break;
					case 'x': _clar_suites[j].enabled = 0; break;
Vicent Marti committed
352
					}
Russell Belfer committed
353 354 355

					if (exact)
						break;
Vicent Marti committed
356 357 358
				}
			}

Russell Belfer committed
359
			if (!found) {
Vicent Marti committed
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
				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);
		}

383 384 385 386
		case 'v':
			_clar.report_suite_names = 1;
			break;

Vicent Marti committed
387 388 389 390 391 392
		default:
			clar_usage(argv[0]);
		}
	}
}

Edward Thomson committed
393 394
void
clar_test_init(int argc, char **argv)
Vicent Marti committed
395 396 397 398 399 400 401 402 403 404 405 406
{
	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
407 408
	_clar.argc = argc;
	_clar.argv = argv;
Edward Thomson committed
409
}
Vicent Marti committed
410

Edward Thomson committed
411 412 413
int
clar_test_run()
{
Edward Thomson committed
414 415 416
	if (_clar.argc > 1)
		clar_parse_args(_clar.argc, _clar.argv);

Vicent Marti committed
417 418 419
	if (!_clar.suites_ran) {
		size_t i;
		for (i = 0; i < _clar_suite_count; ++i)
420
			clar_run_suite(&_clar_suites[i], NULL);
Vicent Marti committed
421 422
	}

Edward Thomson committed
423 424 425 426 427 428
	return _clar.total_errors;
}

void
clar_test_shutdown()
{
Vicent Marti committed
429 430 431 432 433 434 435
	clar_print_shutdown(
		_clar.tests_ran,
		(int)_clar_suite_count,
		_clar.total_errors
	);

	clar_unsandbox();
Edward Thomson committed
436 437 438 439 440 441 442 443 444 445 446 447
}

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
448 449
}

Vicent Marti committed
450 451 452 453 454 455 456 457 458
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);
	}

459
	CL_TRACE(CL_TRACE__TEST__LONGJMP);
Vicent Marti committed
460 461 462 463 464 465 466 467 468 469
	longjmp(_clar.trampoline, -1);
}

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

Russell Belfer committed
470
void clar__fail(
Vicent Marti committed
471 472 473 474 475 476
	const char *file,
	int line,
	const char *error_msg,
	const char *description,
	int should_abort)
{
Russell Belfer committed
477
	struct clar_error *error = calloc(1, sizeof(struct clar_error));
Vicent Marti committed
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497

	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
498
	_clar.test_status = CL_TEST_FAILURE;
Vicent Marti committed
499

Vicent Marti committed
500 501
	if (should_abort)
		abort_test();
Vicent Marti committed
502 503
}

Russell Belfer committed
504 505 506 507 508 509 510 511 512 513 514 515 516 517
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
518
void clar__assert_equal(
Vicent Marti committed
519 520 521
	const char *file,
	int line,
	const char *err,
Russell Belfer committed
522 523 524
	int should_abort,
	const char *fmt,
	...)
Vicent Marti committed
525
{
Russell Belfer committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
	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
549 550 551
	else if(!strcmp("%.*s", fmt)) {
		const char *s1 = va_arg(args, const char *);
		const char *s2 = va_arg(args, const char *);
Edward Thomson committed
552
		int len = va_arg(args, int);
Edward Thomson committed
553 554 555 556
		is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp(s1, s2, len);

		if (!is_equal) {
			if (s1 && s2) {
Edward Thomson committed
557
				int pos;
Edward Thomson committed
558 559 560 561 562 563 564 565 566
				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);
			}
		}
	}
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
	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);
			}
		}
	}
602
	else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
Russell Belfer committed
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
		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);
624
		}
Vicent Marti committed
625 626
	}

Russell Belfer committed
627 628 629
	va_end(args);

	if (!is_equal)
Russell Belfer committed
630
		clar__fail(file, line, err, buf, should_abort);
Vicent Marti committed
631 632 633 634 635 636 637 638 639 640 641 642
}

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"