Skip to content

Commit

Permalink
Make it so asserts no longer nest. That was needless complexity.
Browse files Browse the repository at this point in the history
  • Loading branch information
Scott Bronson committed May 23, 2008
1 parent 6695610 commit f0d43e3
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 71 deletions.
28 changes: 12 additions & 16 deletions CHANGES
@@ -1,5 +1,7 @@
TODO:
- Get setjmp.h and
- It's lame that I need to declare the test function and start the test
separately. Make a macro so I can just cunit_test(ints) { ... }
- Create a prefs structure instead of just having globals scattered everywhere.
- Add routine to parse cmdline args.
--test will initiate unit testing (optional arg).
--list lists unit tests,
Expand All @@ -14,17 +16,15 @@ TODO:
* quiet=3, summary not printed.
- Add some functional tests to check the command line arguments.
- Add ability to easily run tests with different levels of verbosity.
- Make sure to try compiling with gcc set to pedantic C99 as well.
- Add the ability to temporarily disable tests but leave them in the test deck.
- Add ability to run only the named test or tests.
--skip or --only to select tests to run,
- Write the fork-to-test-assert-fail-outside-ctest_start code.
Nope! No forking. Too complex. Maybe can write a func test for this?
- Don't malloc on every test and assert. Keep a cache or something.
- combine some of these stupid tiny files.
- Add support for the NOTESTS macro.
- Make localizable by allowing CTBECAUSE to be redefined
And also remove "is" and "and" from test macros.
- Make tests check for signals. int/fp /0 and bus errors.
- Create a prefs structure instead of just having globals scattered everywhere.
- Add the ability to temporarily disable tests but leave them in the test deck.
- Add ability to specify an optional timeout.
- There's got to be a better API than show_inversions
- Add an epsilon to the floating point comparisons?
This is a little scary... it can confuse the heck out of you if
Expand All @@ -33,17 +33,13 @@ TODO:
It won't be easy... They work well.
- Make ctest's asserts behave more like regular asserts when used outside
a ctest_start block.
- Add the ability to fork before each test. It's a pretty useless feature
- Add ability to run only the named test or tests.
--skip or --only to select tests to run,
but some guys seem pretty hot about this.
- Can I get rid of nesting asserts? That would make things simpler.
- It's lame that I need to declare the test function and start the test
separately. Make a macro so I can just cunit_test(ints) { ... }
- Add the optional ability to fork before each test. It's a pretty useless
feature but some guys seem pretty hot about this.
- Optionally install signal handlers. int/fp /0 and bus errors.
Can we recover just like we do for failed asserts?
- Add ability to specify an optional timeout.
- Fix ctest_show_failures, so gross
- Make a homepage for it, replace the URL in the readme file.
- Instead of creating an implicit test structure, I should just prepare
the assert without the test and then worry about it if it fails.

Version 0.8, IN PROGRESS
- Renamed mutest to ctest. A generic name is better than a lame one.
Expand Down
2 changes: 1 addition & 1 deletion COPYING
@@ -1,4 +1,4 @@
The MIT License
CTest is released under the MIT License.

Copyright (c) 2008 Scott Bronson

Expand Down
81 changes: 27 additions & 54 deletions ctest.c
Expand Up @@ -70,6 +70,12 @@ static int assertion_successes = 0;
/** The number of assertions that have failed. */
static int assertion_failures = 0;

/** the filename of the current assertion (as given by the prepare function) */
const char *assert_file;
/** the line number of the current assertion */
int assert_line;


/** Set this to 1 to print the failures. This allows you to view the output of each failure to ensure it looks OK. */
static int show_failures = 0;
static int verbose = 0;
Expand Down Expand Up @@ -139,58 +145,25 @@ static void test_print(const char *fmt, ...)
}



struct assert {
/** Links to the next assertion in the chain */
struct assert *next;
/** the filename of the current assertion (as given by the prepare function) */
const char *file;
/** the line number of the current assertion */
int line;
};
/** assertions are listed off this list head, from most nested to least nested. */
struct assert *assert_head;


/** Add an assert to the list of asserts currently being checked.
*
* Why do asserts nest? I guess because sometimes you want to create an
* assert based on other asserts. TODO: can nesting asserts be removed?
*/

static void assert_push(struct assert *assert)
{
assert->next = assert_head;
assert_head = assert;
}

/** Pop the topmost assert and dispose of it, move the next one into place */
static void assert_pop()
{
struct assert *assert = assert_head;
assert_head = assert->next;
free(assert);
}


/** Prepare to test an assertion. This does all the common work
* like ensuring a test is ready to go and printing the status..
*/

void ctest_assert_prepare(const char *file, int line, const char *assertion)
{
struct assert* assert;
if(!file) {
fprintf(stderr, "A null file was passed to ctest_assert_prepare!\n");
exit(240);
}

assert = malloc(sizeof(struct assert));
if(!assert) {
fprintf(stderr, "Out of memory allocating struct assert!\n");
exit(244);
if(assert_file) {
fprintf(stderr, "Tried to prepare an assert at %s:%d but the previous assert "
"at %s:%d hasn't finished!\n", file, line, assert_file, assert_line);
exit(237);
}

assert->file = file;
assert->line = line;

assert_push(assert);

assert_file = file;
assert_line = line;

assertions_run += 1;
test_print("%d. checking %s at %s:%d\n",
Expand All @@ -206,24 +179,24 @@ void ctest_assert_failed(const char *msg, ...)
{
va_list ap;

if(!assert_head) {
if(!assert_file) {
fprintf(stderr, "assert failed without ctest_assert_prepare being called first!\n");
exit(243);
exit(242);
}

if(!(test_head && test_head->inverted) || show_failures) {
fprintf(stderr, "%s:%d: assert ", assert_head->file, assert_head->line);
fprintf(stderr, "%s:%d: assert ", assert_file, assert_line);
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
fputc('\n', stderr);
}

assert_pop();
assert_file = NULL;

if(!test_head) {
/* Assert failed and wasn't wrapped in a test. Bail immediatley. */
exit(1);
exit(1); /* 1 means a single test failed */
}

if(test_head->inverted) {
Expand All @@ -239,12 +212,12 @@ void ctest_assert_failed(const char *msg, ...)

void ctest_assert_succeeded()
{
if(!assert_head) {
if(!assert_file) {
fprintf(stderr, "assert succeeded without ctest_assert_prepare being called first!\n");
exit(241);
}

assert_pop();
assert_file = NULL;

if(test_head && test_head->inverted) {
assertion_failures += 1;
Expand All @@ -267,9 +240,9 @@ static void ctest_start_test(const char *name, const char *file, int line, const
name = "(unnamed)";
}

if(assert_head) {
if(assert_file) {
fprintf(stderr, "Previous assertion at %s:%d hasn't reported a result!\n",
assert_head->file, assert_head->line);
assert_file, assert_line);
fprintf(stderr, "Unable to start a new test %s at %s:%d.\n",
name, file, line);
exit(238);
Expand Down Expand Up @@ -333,7 +306,7 @@ int ctest_internal_test_finished(const char *name)
if(!test_head) {
/* how could we end up here without a test_head?? */
fprintf(stderr, "Internal finish error: somehow ctest_start didn't complete?\n");
exit(244);
exit(243);
}

if(!test_head->finished) {
Expand Down

0 comments on commit f0d43e3

Please sign in to comment.