clar.c 11.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 14 15 16 17 18 19 20 21 22 23 24 25 26
#include <assert.h>
#include <setjmp.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdarg.h>

/* 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
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
#	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
45 46 47

#	ifndef __MINGW32__
#		pragma comment(lib, "shell32")
Russell Belfer committed
48 49 50 51 52 53 54 55 56 57
#		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
58
#	else
Russell Belfer committed
59 60 61 62 63 64 65 66
#		define p_snprintf snprintf
#	endif

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

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

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

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

	int tests_ran;
	int suites_ran;

	int report_errors_only;
	int exit_on_error;
Russell Belfer committed
124
	int report_suite_names;
Vicent Marti committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153

	struct clar_error *errors;
	struct clar_error *last_error;

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

	jmp_buf trampoline;
	int trampoline_enabled;
} _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
154
static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed);
Vicent Marti committed
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
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"

/* 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
190
	_clar.test_status = CL_TEST_OK;
Vicent Marti committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
	_clar.trampoline_enabled = 1;

	if (setjmp(_clar.trampoline) == 0) {
		if (initialize->ptr != NULL)
			initialize->ptr();

		test->ptr();
	}

	_clar.trampoline_enabled = 0;

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

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

	_clar.tests_ran++;

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

Vicent Marti committed
214
	if (_clar.report_errors_only) {
Vicent Marti committed
215
		clar_report_errors();
Vicent Marti committed
216 217 218
	} else {
		clar_print_ontest(test->name, _clar.tests_ran, _clar.test_status);
	}
Vicent Marti committed
219 220 221
}

static void
222
clar_run_suite(const struct clar_suite *suite, const char *filter)
Vicent Marti committed
223 224
{
	const struct clar_func *test = suite->tests;
225
	size_t i, matchlen;
Vicent Marti committed
226 227 228 229 230 231 232 233 234 235 236 237

	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;

238
	if (filter) {
239
		size_t suitelen = strlen(suite->name);
240 241 242
		matchlen = strlen(filter);
		if (matchlen <= suitelen) {
			filter = NULL;
243
		} else {
244 245 246 247
			filter += suitelen;
			while (*filter == ':')
				++filter;
			matchlen = strlen(filter);
248 249 250
		}
	}

Vicent Marti committed
251
	for (i = 0; i < suite->test_count; ++i) {
252
		if (filter && strncmp(test[i].name, filter, matchlen))
253 254
			continue;

Vicent Marti committed
255 256 257 258 259 260 261 262 263 264 265 266 267
		_clar.active_test = test[i].name;
		clar_run_test(&test[i], &suite->initialize, &suite->cleanup);

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

static void
clar_usage(const char *arg)
{
	printf("Usage: %s [options]\n\n", arg);
	printf("Options:\n");
268
	printf("  -sname\tRun only the suite with `name` (can go to individual test name)\n");
Russell Belfer committed
269 270 271 272 273
	printf("  -iname\tInclude the suite with `name`\n");
	printf("  -xname\tExclude the suite with `name`\n");
	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
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	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
292
			int offset = (argument[2] == '=') ? 3 : 2, found = 0;
Vicent Marti committed
293
			char action = argument[1];
294
			size_t j, arglen, suitelen, cmplen;
Vicent Marti committed
295 296

			argument += offset;
297
			arglen = strlen(argument);
Vicent Marti committed
298

299
			if (arglen == 0)
Vicent Marti committed
300 301 302
				clar_usage(argv[0]);

			for (j = 0; j < _clar_suite_count; ++j) {
303 304
				suitelen = strlen(_clar_suites[j].name);
				cmplen = (arglen < suitelen) ? arglen : suitelen;
305 306

				if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
307
					int exact = (arglen >= suitelen);
Russell Belfer committed
308 309 310 311 312 313

					++found;

					if (!exact)
						_clar.report_suite_names = 1;

Vicent Marti committed
314
					switch (action) {
315 316 317
					case 's': clar_run_suite(&_clar_suites[j], argument); break;
					case 'i': _clar_suites[j].enabled = 1; break;
					case 'x': _clar_suites[j].enabled = 0; break;
Vicent Marti committed
318
					}
Russell Belfer committed
319 320 321

					if (exact)
						break;
Vicent Marti committed
322 323 324
				}
			}

Russell Belfer committed
325
			if (!found) {
Vicent Marti committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
				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);
		}

		default:
			clar_usage(argv[0]);
		}
	}
}

Edward Thomson committed
355 356
void
clar_test_init(int argc, char **argv)
Vicent Marti committed
357 358 359 360 361 362 363 364 365 366 367 368
{
	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
369 370
	_clar.argc = argc;
	_clar.argv = argv;
Edward Thomson committed
371
}
Vicent Marti committed
372

Edward Thomson committed
373 374 375
int
clar_test_run()
{
Edward Thomson committed
376 377 378
	if (_clar.argc > 1)
		clar_parse_args(_clar.argc, _clar.argv);

Vicent Marti committed
379 380 381
	if (!_clar.suites_ran) {
		size_t i;
		for (i = 0; i < _clar_suite_count; ++i)
382
			clar_run_suite(&_clar_suites[i], NULL);
Vicent Marti committed
383 384
	}

Edward Thomson committed
385 386 387 388 389 390
	return _clar.total_errors;
}

void
clar_test_shutdown()
{
Vicent Marti committed
391 392 393 394 395 396 397
	clar_print_shutdown(
		_clar.tests_ran,
		(int)_clar_suite_count,
		_clar.total_errors
	);

	clar_unsandbox();
Edward Thomson committed
398 399 400 401 402 403 404 405 406 407 408 409
}

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
410 411
}

Vicent Marti committed
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
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);
	}

	longjmp(_clar.trampoline, -1);
}

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

Russell Belfer committed
431
void clar__fail(
Vicent Marti committed
432 433 434 435 436 437
	const char *file,
	int line,
	const char *error_msg,
	const char *description,
	int should_abort)
{
Russell Belfer committed
438
	struct clar_error *error = calloc(1, sizeof(struct clar_error));
Vicent Marti committed
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458

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

Vicent Marti committed
461 462
	if (should_abort)
		abort_test();
Vicent Marti committed
463 464
}

Russell Belfer committed
465 466 467 468 469 470 471 472 473 474 475 476 477 478
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
479
void clar__assert_equal(
Vicent Marti committed
480 481 482
	const char *file,
	int line,
	const char *err,
Russell Belfer committed
483 484 485
	int should_abort,
	const char *fmt,
	...)
Vicent Marti committed
486
{
Russell Belfer committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	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
510 511 512
	else if(!strcmp("%.*s", fmt)) {
		const char *s1 = va_arg(args, const char *);
		const char *s2 = va_arg(args, const char *);
Edward Thomson committed
513
		int len = va_arg(args, int);
Edward Thomson committed
514 515 516 517
		is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp(s1, s2, len);

		if (!is_equal) {
			if (s1 && s2) {
Edward Thomson committed
518
				int pos;
Edward Thomson committed
519 520 521 522 523 524 525 526 527
				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);
			}
		}
	}
528
	else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
Russell Belfer committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
		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);
550
		}
Vicent Marti committed
551 552
	}

Russell Belfer committed
553 554 555
	va_end(args);

	if (!is_equal)
Russell Belfer committed
556
		clar__fail(file, line, err, buf, should_abort);
Vicent Marti committed
557 558 559 560 561 562 563 564 565 566 567 568
}

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"