Compare commits
68 Commits
1fcbeaef5b
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 51dd02ea71 | |||
| 17ad81f6b6 | |||
| c6dd2559f8 | |||
| 3b616f2e22 | |||
| 23ed415852 | |||
| daf189dc8d | |||
| 2054ab3096 | |||
| 45d2801362 | |||
| c3f7ac96cd | |||
| a506b8bd40 | |||
| 9cdc474456 | |||
| 7e0c088cf0 | |||
| 0ffc08fc41 | |||
| 6539b87ea9 | |||
| 00ffef2f0a | |||
| cb3788cf02 | |||
| e5cad7cd20 | |||
| da33bd8955 | |||
| ebf398fd33 | |||
| bea9a3e34a | |||
| 57e4d2ba49 | |||
| 01e9cb8794 | |||
| 8828a8ab17 | |||
| 24ed9060ba | |||
| eaa8ae06a7 | |||
| f808eb4cb0 | |||
| 55926ec0d8 | |||
| 01e01243dd | |||
| 291d25927c | |||
| ad4abe68e0 | |||
| 1c4fb1e87d | |||
| 8138f59fef | |||
| cd590339b7 | |||
| 2011b75b7e | |||
| bd535791d7 | |||
| 6c191d8405 | |||
| 37ce34b160 | |||
| 5d67b7aeb8 | |||
| 7818e3d646 | |||
| a7351b5667 | |||
| 0ad79ea3b0 | |||
| ef00063ba3 | |||
| b9cac45eba | |||
| cead3f67d0 | |||
| 85a6d49b38 | |||
| 1d306f32c0 | |||
| bcfd584608 | |||
| 2a092d1050 | |||
| a772db08ce | |||
| 47638ef0b7 | |||
| 580af62244 | |||
| 3d01638da0 | |||
| 4ff1aa2d6d | |||
| 5e8b1b73c7 | |||
| fc167d8d05 | |||
| 586bbe71ed | |||
| e41263adf0 | |||
| 94d6bc8cf9 | |||
| 3393c3b9af | |||
| 63f0bd73e4 | |||
| 9f4a51d8c7 | |||
| c0f28d41b0 | |||
| 3309ac7aee | |||
| a14b11e853 | |||
| 4c0d33693b | |||
| a962eff7b4 | |||
| c3bace917d | |||
| f30c093a81 |
@@ -25,48 +25,104 @@
|
|||||||
|
|
||||||
#include "9unit.h"
|
#include "9unit.h"
|
||||||
|
|
||||||
|
// Internal Types
|
||||||
|
|
||||||
|
typedef struct RunTestWith RunTestWith;
|
||||||
|
typedef struct RunTestCompare RunTestCompare;
|
||||||
|
typedef struct ContextData ContextData;
|
||||||
|
typedef struct TestContextWith TestContextWith;
|
||||||
|
typedef struct TestContextCompare TestContextCompare;
|
||||||
|
typedef struct SingleTestContextWith SingleTestContextWith;
|
||||||
|
typedef struct SingleTestContextCompare SingleTestContextCompare;
|
||||||
|
|
||||||
|
// data required by run_test_with()
|
||||||
|
struct RunTestWith
|
||||||
|
{
|
||||||
|
void *ptr; // the state's previous ptr value
|
||||||
|
TestResult (*test)(TestState *, void *); // the test
|
||||||
|
void *val; // the value passed in
|
||||||
|
};
|
||||||
|
|
||||||
|
// data needed by run_test_compare()
|
||||||
|
struct RunTestCompare
|
||||||
|
{
|
||||||
|
TestResult (*test)(TestState *, void *, void *);
|
||||||
|
void *val1;
|
||||||
|
void *val2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ContextData
|
||||||
|
{
|
||||||
|
const char *old_c; // previous context
|
||||||
|
const char *old_fc; // previous full context
|
||||||
|
const char *new_c; // new context
|
||||||
|
char *new_fc; // new full context
|
||||||
|
};
|
||||||
|
|
||||||
|
// data needed by test_context_with()
|
||||||
|
struct TestContextWith
|
||||||
|
{
|
||||||
|
void *ptr; // state's original ptr value
|
||||||
|
void (*test)(TestState *, void *); // the test function
|
||||||
|
void *val; // the value being passed in
|
||||||
|
};
|
||||||
|
|
||||||
|
// data needed by test_context_compare()
|
||||||
|
struct TestContextCompare
|
||||||
|
{
|
||||||
|
void (*test)(TestState *, void *, void *);
|
||||||
|
void *val1;
|
||||||
|
void *val2;
|
||||||
|
};
|
||||||
|
|
||||||
|
// data needed by single_test_context_with()
|
||||||
|
struct SingleTestContextWith
|
||||||
|
{
|
||||||
|
TestResult (*test)(TestState *, void *);
|
||||||
|
void *val;
|
||||||
|
};
|
||||||
|
|
||||||
|
// data needed by single_test_context_compare()
|
||||||
|
struct SingleTestContextCompare
|
||||||
|
{
|
||||||
|
TestResult (*test)(TestState *, void *, void *);
|
||||||
|
void *val1;
|
||||||
|
void *val2;
|
||||||
|
};
|
||||||
|
|
||||||
// Internal Prototypes
|
// Internal Prototypes
|
||||||
|
|
||||||
static void init_TestState(TestState *);
|
static void init_TestState(TestState *);
|
||||||
static void print_log(TestState *);
|
static void print_log(TestState *);
|
||||||
static void clear_log(TestState *);
|
static void clear_log(TestState *);
|
||||||
static void reindex(TestState *);
|
static void reindex(TestState *);
|
||||||
|
static void report(const char *);
|
||||||
|
static void build_new_context(TestState *, ContextData *);
|
||||||
|
static void display_context(TestState *);
|
||||||
|
static void restore_context(TestState *, ContextData *);
|
||||||
|
static TestResult run_test_with_test(TestState *);
|
||||||
|
static TestResult run_test_compare_test(TestState *, void *);
|
||||||
|
static void test_context_with_test(TestState *);
|
||||||
|
static void test_context_compare_test(TestState *, void *);
|
||||||
|
static void single_test_context_test(TestState *, void *);
|
||||||
|
static void single_test_context_with_test(TestState *, void *);
|
||||||
|
static TestResult single_test_context_compare_test(TestState *, void *);
|
||||||
|
|
||||||
// Public Functions
|
// Public Functions
|
||||||
|
|
||||||
void
|
|
||||||
run_test(TestState *s, TestResult (*t)(TestState *))
|
|
||||||
{
|
|
||||||
if (!(s && t)) return;
|
|
||||||
s->run++;
|
|
||||||
switch ((*t)(s))
|
|
||||||
{
|
|
||||||
case test_success:
|
|
||||||
s->passed++;
|
|
||||||
break;
|
|
||||||
case test_failure:
|
|
||||||
s->failed++;
|
|
||||||
break;
|
|
||||||
case test_pending:
|
|
||||||
s->pending++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
exits("test returned an invalid response");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
run_tests(void (*tests)(TestState *))
|
run_tests(void (*tests)(TestState *))
|
||||||
{
|
{
|
||||||
if (!tests) return;
|
if (!tests) return;
|
||||||
TestState s;
|
TestState s;
|
||||||
init_TestState(&s);
|
memset(&s, 0, sizeof(TestState));
|
||||||
|
s.report = report;
|
||||||
(*tests)(&s);
|
(*tests)(&s);
|
||||||
print_log(&s);
|
print_log(&s);
|
||||||
printf("Tests run: %d\n", s.run);
|
print("\n\nTests run: %d\n", s.run);
|
||||||
printf("Tests passed: %d\n", s.passed);
|
print("Tests passed: %d\n", s.passed);
|
||||||
printf("Tests failed: %d\n", s.failed);
|
print("Tests failed: %d\n", s.failed);
|
||||||
printf("Tests pending: %d\n", s.pending);
|
print("Tests pending: %d\n", s.pending);
|
||||||
clear_log(&s);
|
clear_log(&s);
|
||||||
if (s.failed) exits("test(s) failed");
|
if (s.failed) exits("test(s) failed");
|
||||||
}
|
}
|
||||||
@@ -101,21 +157,192 @@ append_test_log(TestState *s, const char *msg)
|
|||||||
s->last_log = entry;
|
s->last_log = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal Functions
|
void
|
||||||
|
log_test_context(TestState *s)
|
||||||
static void
|
|
||||||
init_TestState(TestState *s)
|
|
||||||
{
|
{
|
||||||
if (!s) return;
|
if (!s) return;
|
||||||
s->run = 0;
|
if (s->full_context) append_test_log(s, s->full_context);
|
||||||
s->passed = 0;
|
else append_test_log(s, "<no context>");
|
||||||
s->failed = 0;
|
|
||||||
s->pending = 0;
|
|
||||||
s->first_log = 0;
|
|
||||||
s->last_log = 0;
|
|
||||||
s->ptr = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_test(TestState *s, TestResult (*t)(TestState *))
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
s->run++;
|
||||||
|
|
||||||
|
// a null test function is a pending test
|
||||||
|
if (!t)
|
||||||
|
{
|
||||||
|
s->pending++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((*t)(s))
|
||||||
|
{
|
||||||
|
case test_success:
|
||||||
|
s->passed++;
|
||||||
|
s->report(" [PASS]");
|
||||||
|
break;
|
||||||
|
case test_failure:
|
||||||
|
s->failed++;
|
||||||
|
s->report(" [FAIL]");
|
||||||
|
break;
|
||||||
|
case test_pending:
|
||||||
|
s->pending++;
|
||||||
|
s->report(" [PEND]");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
exits("test returned an invalid response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_test_with(
|
||||||
|
TestState *s, // the state
|
||||||
|
TestResult (*test)(TestState *, void *), // the test
|
||||||
|
void *val // the value being passed in
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
if (!test)
|
||||||
|
{
|
||||||
|
run_test(s, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RunTestWith d;
|
||||||
|
d.ptr = s->ptr;
|
||||||
|
d.test = test;
|
||||||
|
d.val = val;
|
||||||
|
s->ptr = &d;
|
||||||
|
run_test(s, run_test_with_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run_test_compare(
|
||||||
|
TestState *s,
|
||||||
|
TestResult (*test)(TestState *, void *, void *),
|
||||||
|
void *val1,
|
||||||
|
void *val2
|
||||||
|
)
|
||||||
|
{
|
||||||
|
RunTestCompare d;
|
||||||
|
d.test = test;
|
||||||
|
d.val1 = val1;
|
||||||
|
d.val2 = val2;
|
||||||
|
run_test_with(s, run_test_compare_test, &d);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_context(
|
||||||
|
TestState *s,
|
||||||
|
const char *label,
|
||||||
|
void (*test)(TestState *)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!(s && test)) return;
|
||||||
|
if (label)
|
||||||
|
{
|
||||||
|
ContextData cd;
|
||||||
|
cd.new_c = label;
|
||||||
|
build_new_context(s, &cd);
|
||||||
|
display_context(s);
|
||||||
|
(*test)(s);
|
||||||
|
restore_context(s, &cd);
|
||||||
|
}
|
||||||
|
else (*test)(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_context_with(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
void (*test)(TestState *, void *),
|
||||||
|
void *val
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
TestContextWith d;
|
||||||
|
d.ptr = s->ptr;
|
||||||
|
d.test = test;
|
||||||
|
d.val = val;
|
||||||
|
s->ptr = &d;
|
||||||
|
test_context(s, context, test_context_with_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test_context_compare(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
void (*test)(TestState *, void *, void *),
|
||||||
|
void *val1,
|
||||||
|
void *val2
|
||||||
|
)
|
||||||
|
{
|
||||||
|
TestContextCompare d;
|
||||||
|
d.test = test;
|
||||||
|
d.val1 = val1;
|
||||||
|
d.val2 = val2;
|
||||||
|
test_context_with(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
test_context_compare_test,
|
||||||
|
&d
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
single_test_context(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
TestResult (*test)(TestState *)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
test_context_with(s, context, single_test_context_test, test);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
single_test_context_with(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
TestResult (*test)(TestState *, void *),
|
||||||
|
void *val
|
||||||
|
)
|
||||||
|
{
|
||||||
|
SingleTestContextWith d;
|
||||||
|
d.test = test;
|
||||||
|
d.val = val;
|
||||||
|
test_context_with(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
single_test_context_with_test,
|
||||||
|
&d
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
single_test_context_compare(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
TestResult (*test)(TestState *s, void *, void *),
|
||||||
|
void *val1,
|
||||||
|
void *val2
|
||||||
|
)
|
||||||
|
{
|
||||||
|
SingleTestContextCompare d;
|
||||||
|
d.test = test;
|
||||||
|
d.val1 = val1;
|
||||||
|
d.val2 = val2;
|
||||||
|
single_test_context_with(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
single_test_context_compare_test,
|
||||||
|
&d
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Functions
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_log(TestState *s)
|
print_log(TestState *s)
|
||||||
{
|
{
|
||||||
@@ -123,7 +350,7 @@ print_log(TestState *s)
|
|||||||
TestLogEntry *e = s->first_log;
|
TestLogEntry *e = s->first_log;
|
||||||
while(e)
|
while(e)
|
||||||
{
|
{
|
||||||
if(e->text) printf("%s\n", e->text);
|
if(e->text) print("\n%s", e->text);
|
||||||
else print("(empty message)\n");
|
else print("(empty message)\n");
|
||||||
e = e->next;
|
e = e->next;
|
||||||
}
|
}
|
||||||
@@ -162,8 +389,109 @@ reindex(TestState *s)
|
|||||||
else if (s->last_log) // we have a last log but no first?
|
else if (s->last_log) // we have a last log but no first?
|
||||||
{
|
{
|
||||||
s->first_log = s->last_log;
|
s->first_log = s->last_log;
|
||||||
fprint(2, "potential memory leak in test log\n");
|
log_test_context(s);
|
||||||
|
append_test_log(s, "potential memory leak in test log");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
report(const char *str)
|
||||||
|
{
|
||||||
|
print("%s", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
build_new_context(TestState *s, ContextData *cd)
|
||||||
|
{
|
||||||
|
cd->old_c = s->context;
|
||||||
|
cd->old_fc = s->full_context;
|
||||||
|
if (s->full_context)
|
||||||
|
{
|
||||||
|
cd->new_fc = malloc(strlen(cd->old_fc) + strlen(cd->new_c) + 3);
|
||||||
|
sprintf(cd->new_fc, "%s: %s", cd->old_fc, cd->new_c);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cd->new_fc = malloc(strlen(cd->new_c) + 1);
|
||||||
|
strcpy(cd->new_fc, cd->new_c);
|
||||||
|
}
|
||||||
|
s->context = cd->new_c;
|
||||||
|
s->full_context = cd->new_fc;
|
||||||
|
s->depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
display_context(TestState *s)
|
||||||
|
{
|
||||||
|
s->report("\n");
|
||||||
|
for (int i = 1; i < s->depth; i++)
|
||||||
|
s->report("\t");
|
||||||
|
s->report(s->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
restore_context(TestState *s, ContextData *cd)
|
||||||
|
{
|
||||||
|
s->context = cd->old_c;
|
||||||
|
s->full_context = cd->old_fc;
|
||||||
|
s->depth--;
|
||||||
|
free(cd->new_fc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
run_test_with_test(TestState *s)
|
||||||
|
{
|
||||||
|
RunTestWith *d = s->ptr;
|
||||||
|
s->ptr = d->ptr;
|
||||||
|
return (*d->test)(s, d->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
run_test_compare_test(
|
||||||
|
TestState *s,
|
||||||
|
void *ptr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
RunTestCompare *d = ptr;
|
||||||
|
return (*d->test)(s, d->val1, d->val2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_context_with_test(TestState *s)
|
||||||
|
{
|
||||||
|
TestContextWith *d = s->ptr;
|
||||||
|
s->ptr = d->ptr;
|
||||||
|
(*d->test)(s, d->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_context_compare_test(
|
||||||
|
TestState *s,
|
||||||
|
void *ptr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
TestContextCompare *d = ptr;
|
||||||
|
(*d->test)(s, d->val1, d->val2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
single_test_context_test(TestState *s, void *test)
|
||||||
|
{
|
||||||
|
run_test(s, test);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
single_test_context_with_test(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
SingleTestContextWith *d = ptr;
|
||||||
|
run_test_with(s, d->test, d->val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
single_test_context_compare_test(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
SingleTestContextCompare *d = ptr;
|
||||||
|
return (*d->test)(s, d->val1, d->val2);
|
||||||
|
}
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
@@ -19,31 +19,11 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Tracks information about the tests being run.
|
// Types & Structs
|
||||||
typedef struct TestState TestState;
|
|
||||||
|
|
||||||
// Defines a log entry in a TestState struct
|
|
||||||
typedef struct TestLogEntry TestLogEntry;
|
|
||||||
|
|
||||||
// The following structures will typically be maintained by the
|
// The following structures will typically be maintained by the
|
||||||
// testing framework. You shouldn't need to concern yourself with
|
// testing framework. You shouldn't need to concern yourself with
|
||||||
// them.
|
// them.
|
||||||
struct TestState
|
|
||||||
{
|
|
||||||
int run; // number of tests run
|
|
||||||
int passed; // number of successful tests
|
|
||||||
int failed; // number of failed tests
|
|
||||||
int pending; // number of pending tests
|
|
||||||
TestLogEntry *first_log; // the first log entry
|
|
||||||
TestLogEntry *last_log; //the last log entry
|
|
||||||
void *ptr; // used for passing data between tests
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TestLogEntry
|
|
||||||
{
|
|
||||||
char *text; // the entry text
|
|
||||||
TestLogEntry *next; // points to the next entry
|
|
||||||
};
|
|
||||||
|
|
||||||
// Possible results of running a single test
|
// Possible results of running a single test
|
||||||
typedef enum TestResult
|
typedef enum TestResult
|
||||||
@@ -53,17 +33,37 @@ typedef enum TestResult
|
|||||||
test_pending // the test is pending
|
test_pending // the test is pending
|
||||||
} TestResult;
|
} TestResult;
|
||||||
|
|
||||||
// Runs a single test
|
typedef struct TestState TestState;
|
||||||
extern void run_test(
|
typedef struct TestLogEntry TestLogEntry;
|
||||||
TestState *, // the TestState data
|
|
||||||
TestResult (*)(TestState *) // the test to run
|
|
||||||
);
|
|
||||||
|
|
||||||
// Runs multiple tests, displaying a summary at the end
|
// Tracks information about the tests being run.
|
||||||
extern void run_tests(
|
struct TestState
|
||||||
// runs the tests and updates a provided TestState
|
{
|
||||||
void (*)(TestState *)
|
int run; // number of tests run
|
||||||
);
|
int passed; // number of successful tests
|
||||||
|
int failed; // number of failed tests
|
||||||
|
int pending; // number of pending tests
|
||||||
|
TestLogEntry *first_log; // the first log entry
|
||||||
|
TestLogEntry *last_log; //the last log entry
|
||||||
|
void *ptr; // used for passing data between tests
|
||||||
|
const char *context; // immediate context of current test
|
||||||
|
const char *full_context; // full context of current test
|
||||||
|
int depth; // how many tests "deep" are we?
|
||||||
|
void (*report)(const char *); // prints a string immediately
|
||||||
|
};
|
||||||
|
|
||||||
|
// Defines a log entry in a TestState struct
|
||||||
|
struct TestLogEntry
|
||||||
|
{
|
||||||
|
char *text; // the entry text
|
||||||
|
TestLogEntry *next; // points to the next entry
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
|
||||||
|
// Creates an initial TestState, passes it to the supplied function,
|
||||||
|
// and displays the resulting log and summary
|
||||||
|
extern void run_tests(void (*)(TestState *));
|
||||||
|
|
||||||
// Adds an entry to the log that is displayed after the tests have
|
// Adds an entry to the log that is displayed after the tests have
|
||||||
// completed
|
// completed
|
||||||
@@ -72,4 +72,104 @@ extern void append_test_log(
|
|||||||
const char * // the message to append
|
const char * // the message to append
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// notes the current full context in the log
|
||||||
|
extern void log_test_context(TestState *);
|
||||||
|
|
||||||
|
// Runs a single test
|
||||||
|
extern void run_test(
|
||||||
|
TestState *, // the TestState data
|
||||||
|
TestResult (*)(TestState *) // the test to run
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runs a single test with an arbitrary input
|
||||||
|
extern void run_test_with(
|
||||||
|
TestState *, // the state
|
||||||
|
TestResult (*)(TestState *, void *), // the test
|
||||||
|
void * // the value to pass in
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runs a single test passing in two values to be compared
|
||||||
|
extern void run_test_compare(
|
||||||
|
TestState *, // the current state
|
||||||
|
|
||||||
|
// the test to be run
|
||||||
|
TestResult (*test)(
|
||||||
|
TestState *, // the current state
|
||||||
|
void *, // the first value
|
||||||
|
void * // the second value
|
||||||
|
),
|
||||||
|
|
||||||
|
void *, // the first value
|
||||||
|
void * // the second value
|
||||||
|
);
|
||||||
|
|
||||||
|
// Gives additional context for a test
|
||||||
|
extern void test_context(
|
||||||
|
TestState *, // the current state
|
||||||
|
const char *, // a description of the context
|
||||||
|
void (*)(TestState *) // the actual test
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runs a test with a context and an additional input
|
||||||
|
extern void test_context_with(
|
||||||
|
TestState *, // the current state
|
||||||
|
const char *, // a description of the context
|
||||||
|
void (*)(TestState *, void *), // the test function
|
||||||
|
void * // the value being passed in
|
||||||
|
);
|
||||||
|
|
||||||
|
// Passes two values into a test context
|
||||||
|
void test_context_compare(
|
||||||
|
TestState *, // the current state
|
||||||
|
const char *, // the context label
|
||||||
|
|
||||||
|
// test function
|
||||||
|
void (*)(
|
||||||
|
TestState *, // the current state
|
||||||
|
void *, // the first value
|
||||||
|
void * // the second value
|
||||||
|
),
|
||||||
|
|
||||||
|
void *, // the first value
|
||||||
|
void * // the second value
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runs a single test with a context label
|
||||||
|
extern void single_test_context(
|
||||||
|
TestState *, // the current state
|
||||||
|
const char *, // a description of the context
|
||||||
|
TestResult (*)(TestState *) // the actual test
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runs a single test with a context and input
|
||||||
|
extern void single_test_context_with(
|
||||||
|
TestState *, // the state
|
||||||
|
const char *, // the context
|
||||||
|
|
||||||
|
// the test
|
||||||
|
TestResult (*)(
|
||||||
|
TestState *, // the state
|
||||||
|
void * // the value being passed
|
||||||
|
),
|
||||||
|
|
||||||
|
void * // the value being passed
|
||||||
|
);
|
||||||
|
|
||||||
|
// Runs a single test with a context, passing it two values to be
|
||||||
|
// compared
|
||||||
|
extern void single_test_context_compare(
|
||||||
|
TestState *, // the state
|
||||||
|
const char *, // the context
|
||||||
|
|
||||||
|
// the test function
|
||||||
|
TestResult (*)(
|
||||||
|
TestState *, // the state
|
||||||
|
void *, // the first value
|
||||||
|
void * // the second value
|
||||||
|
),
|
||||||
|
|
||||||
|
void *, // the first value
|
||||||
|
void * // the second value
|
||||||
|
);
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
@@ -1,16 +1,314 @@
|
|||||||
# 9unit
|
# 9unit
|
||||||
Copyright (C) 2023 Jonathan Lamothe <jonathan@jlamothe.net>
|
Copyright (C) 2023 Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
## WARNING
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
This library is experimental and should be considered subject to change at any time.
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
A simple C testing framework for Plan9
|
A simple unit testing framework for C programs in Plan9
|
||||||
|
|
||||||
|
This provides the library file `9unit.a` and the header `9unit.h`.
|
||||||
|
The header is relatively well commented and can provide a fairly
|
||||||
|
comprehensive breakdown of the API. This document will however
|
||||||
|
provide a basic overview below.
|
||||||
|
|
||||||
|
This library is used to test itself, consequently the `test` directory
|
||||||
|
contains a relitively decent real-world (if somewhat confusing)
|
||||||
|
example of how it can be used.
|
||||||
|
|
||||||
|
## `TestState`
|
||||||
|
|
||||||
|
The entire testing framework is centred around the `TestState` data
|
||||||
|
structure. As its name would imply, it contains the current state of
|
||||||
|
the tests in progress, however it should almost never be necessary to
|
||||||
|
interact with it directly. With the exception of `run_tests()`
|
||||||
|
(described below), all functions provided by the library will take
|
||||||
|
take a pointer to the current `TestState` value as their first
|
||||||
|
argument.
|
||||||
|
|
||||||
|
## `run_tests()`
|
||||||
|
|
||||||
|
This will typically be the first function called. It creates an
|
||||||
|
initial `TestState` value, runs the tests, and displays a test log and
|
||||||
|
summary at the end. If any of the tests fail, it will cause the test
|
||||||
|
process to exit with a status of `"test(s) failed"`. Its prototype
|
||||||
|
follows:
|
||||||
|
|
||||||
|
```C
|
||||||
|
void run_tests(void (*)(TestState *));
|
||||||
|
```
|
||||||
|
|
||||||
|
Its only argument is a pointer to a function which is then
|
||||||
|
responsible for actually running the tests. A pointer to the newly
|
||||||
|
created `TestState` value will be passed to this function.
|
||||||
|
|
||||||
|
## Simple Tests
|
||||||
|
|
||||||
|
The simplest form of test can be represented by a function resembling
|
||||||
|
the follwoing:
|
||||||
|
|
||||||
|
```C
|
||||||
|
TestResult my_test(TestState *s)
|
||||||
|
{
|
||||||
|
// test code goes here...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This function should return a `TestResult` value representing (perhaps
|
||||||
|
unsurprisingly) the result of the test. The options are as follows:
|
||||||
|
|
||||||
|
- `test_success`: the test was completed successfully
|
||||||
|
- `test_failure`: the test failed
|
||||||
|
- `test_pending`: the test is pending, and should be ignored for now
|
||||||
|
|
||||||
|
Tests of this type can be run by passing a pointer to them to the
|
||||||
|
`run_test()` function which has the following prototype:
|
||||||
|
|
||||||
|
```C
|
||||||
|
void run_test(
|
||||||
|
TestState *,
|
||||||
|
TestResult (*)(TestState *)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
This function will call the provided test function, and update the
|
||||||
|
provided `TestState` to reflect the result. Thus, the above
|
||||||
|
hypothetical test could by run as follows:
|
||||||
|
|
||||||
|
```C
|
||||||
|
void
|
||||||
|
tests(TestState *s)
|
||||||
|
{
|
||||||
|
run_test(s, my_test);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
run_tests(tests);
|
||||||
|
exits(0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Passing a null `TestState` pointer will cause nothing to happen. This
|
||||||
|
is true of all functions in this library. (This behaviour might be
|
||||||
|
reconsidered later, so don't count on it.) Passing a null function
|
||||||
|
pointer to `run_test()` is interpreted as a pending test.
|
||||||
|
|
||||||
|
## Passing Values to Tests
|
||||||
|
|
||||||
|
Since C supports neither lambdas nor closures, this would leave one
|
||||||
|
with little choice but to come up with a unique name for each
|
||||||
|
individual test function. This, while possible, would definitely be
|
||||||
|
rather inconvenient. To combat this shortcoming, it is helpful to be
|
||||||
|
able to pass data into a generic test function so that it can be
|
||||||
|
reused multiple times.
|
||||||
|
|
||||||
|
### The `ptr` Value
|
||||||
|
|
||||||
|
The `TestState` struct has a value called `ptr` which is a `void`
|
||||||
|
pointer that can be set prior to calling `run_test()` (or any other
|
||||||
|
function, really). This value can then be referenced by the test
|
||||||
|
function, giving you the ability to essentially pass in (or out) *any*
|
||||||
|
type of data you may need. While not ideal, it's *a* solution.
|
||||||
|
|
||||||
|
The library does not perform any kind of validation or automatic
|
||||||
|
memory management on the `ptr` value (this is C after all), so the
|
||||||
|
responsibility for this falls to the programmer implementing the
|
||||||
|
tests.
|
||||||
|
|
||||||
|
### Convenience Functions
|
||||||
|
|
||||||
|
As the test suite becomes more and more complex, managing a single
|
||||||
|
`ptr` value can become increasingly burdensome. For this reason,
|
||||||
|
there are a few convenience functions that provide an alternate
|
||||||
|
mechanism for passing data into a function without altering the `ptr`
|
||||||
|
value. (They actually do alter it internally, but they restore the
|
||||||
|
original value before passing the state on.) Two such functions are:
|
||||||
|
`run_test_with()`, and `run_test_compare()`.
|
||||||
|
|
||||||
|
`run_test_with()` has the following prototype:
|
||||||
|
|
||||||
|
```C
|
||||||
|
void run_test_with(
|
||||||
|
TestState *,
|
||||||
|
TestResult (*)(TestState *, void *),
|
||||||
|
void *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
The first argument points to the current test state. The second
|
||||||
|
points to a test function much like the simple test function described
|
||||||
|
above, but that takes a void pointer as a second argument. Finally,
|
||||||
|
the third argument is the pointer that gets passed into the test
|
||||||
|
function.
|
||||||
|
|
||||||
|
`run_test_compare()` is similar, but it allows *two* pointers to be
|
||||||
|
passed into the test function. This is useful for comparing the
|
||||||
|
actual output of a function to an expected value, for instance.
|
||||||
|
|
||||||
|
The prototype for `run_test_compare()` follows:
|
||||||
|
|
||||||
|
```C
|
||||||
|
void run_test_compare(
|
||||||
|
TestState *,
|
||||||
|
TestResult (*)(TestState *, void *, void *),
|
||||||
|
void *,
|
||||||
|
void *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
The pointers will be passed into the test function in the same order
|
||||||
|
they are passed into `run_test_compare()`.
|
||||||
|
|
||||||
|
## Test Contexts
|
||||||
|
|
||||||
|
It is useful to document what your tests are doing. This can be
|
||||||
|
achieved using contexts. Contexts are essentially labelled
|
||||||
|
collections of related tests. Contexts can be nested to create
|
||||||
|
hierarchies. This is useful both for organization purposes as well as
|
||||||
|
creating reusable test code. There are several functions written for
|
||||||
|
managing these contexts. Each of these functions takes as its first
|
||||||
|
two arguments: a pointer to the current `TestState`, and a pointer to
|
||||||
|
a string describing the context it defines. If the pointer to the
|
||||||
|
string is null, the tests are run as a part of the existing context.
|
||||||
|
|
||||||
|
### `test_context()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void test_context(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
void (*)(TestState *)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function takes a pointer to the current `TestState`, a string
|
||||||
|
describing the context, and a function pointer that is used the same
|
||||||
|
way as the one passed to `run_tests()`. This function will be called
|
||||||
|
and its tests will be run within the newly defined context. Nothing
|
||||||
|
prevents this function from being called again in a different context.
|
||||||
|
|
||||||
|
### `test_context_with()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void test_context_with(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
void (*)(TestState *, void *),
|
||||||
|
void *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This funciton works similarly to `test_context()`, but allows for the
|
||||||
|
passing of a `void` pointer into the test function in much the same
|
||||||
|
way as the `run_test_with()` function. Its arguments are (in order),
|
||||||
|
a pointer to the current state, the context description, a pointer to
|
||||||
|
the test function, and the pointer to be passed into that function.
|
||||||
|
|
||||||
|
### `test_context_compare()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void test_context_compare(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
void (*)(TestState *, void *, void *),
|
||||||
|
void *,
|
||||||
|
void *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This funciton allows the passing to two `void` pointers into a context
|
||||||
|
in a manner similar to `run_test_compare()`.
|
||||||
|
|
||||||
|
### `single_test_context()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void single_test_context(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
TestState (*)(TestState *)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function applies the context label to a *single* test. The
|
||||||
|
function passed in is expected to operate in the same way as the one
|
||||||
|
passed to `run_test()`.
|
||||||
|
|
||||||
|
### `single_test_context_with()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void single_test_context_with(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
TestState (*)(TestState *, void *),
|
||||||
|
void *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This is similar to `single_test_context()` but allows a `void` pointer
|
||||||
|
to be passed as in `run_test_with()`.
|
||||||
|
|
||||||
|
### `single_test_context_compare()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void single_test_context_compare(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
TestResult (*)(TestState *, void *, void *),
|
||||||
|
void *,
|
||||||
|
void *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
I assume you get the idea at this point.
|
||||||
|
|
||||||
|
## Logging
|
||||||
|
|
||||||
|
When `run_tests()` finishes running the tests, it displays a log and
|
||||||
|
summary. The summary is simply a tally of the number of tests run,
|
||||||
|
passed, failed, and pending. While this is useful (and probably all
|
||||||
|
you need to know when all the tests pass) it is likely desirable to
|
||||||
|
have more detail when something goes wrong. To facilitate this, tests
|
||||||
|
can append to the test log, which is automatically displayed just
|
||||||
|
before the summary. There are two functions for doing this.
|
||||||
|
|
||||||
|
### `append_test_log()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void append_test_log(
|
||||||
|
TestState *,
|
||||||
|
const char *
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
This appends an arbitrary string to the end of the test log. The
|
||||||
|
contents of the string are copied into the log, so the value pointed
|
||||||
|
to by the second argument does not need to persist in memory beyond
|
||||||
|
the end of the call to the function. Log entries are expected to be
|
||||||
|
single lines. No trailing newline should be present (but the trailing
|
||||||
|
NUL character should (obviously)).
|
||||||
|
|
||||||
|
### `log_test_context()`
|
||||||
|
|
||||||
|
```C
|
||||||
|
void log_test_context(TestState *);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function appends an entry to the log indicating the test's
|
||||||
|
current *full* context. If no context is defined, the log entry will
|
||||||
|
be `"<no context>"`. If the test is inside of a context labeled
|
||||||
|
`"foo"` which is inside of another context labeled `"bar"`, the
|
||||||
|
resulting log entry will read `"bar: foo"`.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ cleantest:V:
|
|||||||
cd test && mk $MKFLAGS clean
|
cd test && mk $MKFLAGS clean
|
||||||
|
|
||||||
nuketest:V:
|
nuketest:V:
|
||||||
cd test && mk $MKFLAGS clean
|
cd test && mk $MKFLAGS nuke
|
||||||
|
|
||||||
clean:V: cleantest
|
clean:V: cleantest
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,287 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
|
||||||
|
#include "../9unit.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "append-test-log.h"
|
||||||
|
|
||||||
|
// Local Types
|
||||||
|
|
||||||
|
typedef struct LogData LogData;
|
||||||
|
|
||||||
|
struct LogData
|
||||||
|
{
|
||||||
|
TestState state;
|
||||||
|
TestLogEntry first, second;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Local Prototypes
|
||||||
|
|
||||||
|
static void append_to_empty(TestState *);
|
||||||
|
static void append_to_existing(TestState *);
|
||||||
|
static void missing_last(TestState *);
|
||||||
|
static void missing_first(TestState *);
|
||||||
|
static void null_message(TestState *);
|
||||||
|
static TestResult null_state(TestState *);
|
||||||
|
static void chk_empty_first(TestState *, void *);
|
||||||
|
static void chk_first(TestState *, void *);
|
||||||
|
static void chk_second(TestState *, void *);
|
||||||
|
static void chk_last(TestState *, void *);
|
||||||
|
static void mf_chk_first(TestState *, void *);
|
||||||
|
static void mf_chk_second(TestState *, void *);
|
||||||
|
static void mf_chk_third(TestState*, void *);
|
||||||
|
static void mk_log_data(LogData *);
|
||||||
|
static void clean_entry(TestLogEntry *);
|
||||||
|
|
||||||
|
static void chk_ptr_chg(
|
||||||
|
TestState *,
|
||||||
|
const char *,
|
||||||
|
const void *,
|
||||||
|
const void *
|
||||||
|
);
|
||||||
|
|
||||||
|
static void chk_ptr_chg_test(TestState *, void *, void *);
|
||||||
|
|
||||||
|
// Public Functions
|
||||||
|
|
||||||
|
void
|
||||||
|
test_append_test_log(TestState *s)
|
||||||
|
{
|
||||||
|
test_context(s, "append to empty", append_to_empty);
|
||||||
|
test_context(s, "append to existing", append_to_existing);
|
||||||
|
test_context(s, "missing last_log", missing_last);
|
||||||
|
test_context(s, "missing first_log", missing_first);
|
||||||
|
test_context(s, "null message", null_message);
|
||||||
|
single_test_context(s, "null state", null_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local Functions
|
||||||
|
|
||||||
|
static void
|
||||||
|
append_to_empty(TestState *s)
|
||||||
|
{
|
||||||
|
TestState test;
|
||||||
|
mk_sample_state(&test);
|
||||||
|
append_test_log(&test, "foo");
|
||||||
|
|
||||||
|
// first_log
|
||||||
|
test_context_with(
|
||||||
|
s,
|
||||||
|
"first_log",
|
||||||
|
chk_empty_first,
|
||||||
|
&test
|
||||||
|
);
|
||||||
|
|
||||||
|
// last_log
|
||||||
|
chk_ptr_eq(s, "last_log", test.last_log, test.first_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
append_to_existing(TestState *s)
|
||||||
|
{
|
||||||
|
LogData ld;
|
||||||
|
mk_log_data(&ld);
|
||||||
|
append_test_log(&ld.state, "baz");
|
||||||
|
test_context_with(s, "first_log", chk_first, &ld);
|
||||||
|
test_context_with(s, "second log", chk_second, &ld);
|
||||||
|
test_context_with(s, "last_log", chk_last, &ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
missing_last(TestState *s)
|
||||||
|
{
|
||||||
|
LogData ld;
|
||||||
|
mk_log_data(&ld);
|
||||||
|
ld.state.last_log = 0;
|
||||||
|
append_test_log(&ld.state, "baz");
|
||||||
|
test_context_with(s, "first_log", chk_first, &ld);
|
||||||
|
test_context_with(s, "second log", chk_second, &ld);
|
||||||
|
test_context_with(s, "last_log", chk_last, &ld);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
missing_first(TestState *s)
|
||||||
|
{
|
||||||
|
LogData ld;
|
||||||
|
mk_log_data(&ld);
|
||||||
|
ld.state.first_log = 0;
|
||||||
|
append_test_log(&ld.state, "baz");
|
||||||
|
test_context_with(s, "first_log", mf_chk_first, &ld);
|
||||||
|
test_context_with(s, "last_log", chk_last, &ld);
|
||||||
|
TestLogEntry
|
||||||
|
*context = ld.second.next,
|
||||||
|
*warning = context->next;
|
||||||
|
clean_entry(context);
|
||||||
|
clean_entry(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
null_message(TestState *s)
|
||||||
|
{
|
||||||
|
LogData ld;
|
||||||
|
mk_log_data(&ld);
|
||||||
|
append_test_log(&ld.state, 0);
|
||||||
|
chk_ptr_eq(s, "first_log", ld.state.first_log, &ld.first);
|
||||||
|
chk_ptr_eq(s, "last_log", ld.state.last_log, &ld.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
null_state(TestState *)
|
||||||
|
{
|
||||||
|
// ensure it doesn't crash
|
||||||
|
append_test_log(0, "foo");
|
||||||
|
return test_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chk_empty_first(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
TestState *test = ptr;
|
||||||
|
chk_str_eq(s, "text", test->first_log->text, "foo");
|
||||||
|
chk_ptr_eq(s, "next", test->first_log->next, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chk_first(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
LogData *ld = ptr;
|
||||||
|
|
||||||
|
// first_log
|
||||||
|
chk_ptr_eq(
|
||||||
|
s,
|
||||||
|
0,
|
||||||
|
ld->state.first_log,
|
||||||
|
&ld->first
|
||||||
|
);
|
||||||
|
|
||||||
|
// next
|
||||||
|
chk_ptr_eq(
|
||||||
|
s,
|
||||||
|
"next",
|
||||||
|
ld->state.first_log->next,
|
||||||
|
&ld->second
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chk_second(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
LogData *ld = ptr;
|
||||||
|
chk_ptr_eq(
|
||||||
|
s,
|
||||||
|
"next",
|
||||||
|
ld->second.next,
|
||||||
|
ld->state.last_log
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chk_last(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
LogData *ld = ptr;
|
||||||
|
chk_ptr_chg(s, 0, ld->state.last_log, &ld->second);
|
||||||
|
chk_str_eq(s, "text", ld->state.last_log->text, "baz");
|
||||||
|
chk_ptr_eq(s, "next", ld->state.last_log->next, 0);
|
||||||
|
clean_entry(ld->state.last_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mf_chk_first(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
LogData *ld = ptr;
|
||||||
|
chk_ptr_eq(s, 0, ld->state.first_log, &ld->second);
|
||||||
|
test_context_with(s, "next", mf_chk_second, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mf_chk_second(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
LogData *ld = ptr;
|
||||||
|
TestLogEntry *e = ld->second.next;
|
||||||
|
chk_str_eq(s, "text", e->text, "<no context>");
|
||||||
|
test_context_with(s, "next", mf_chk_third, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mf_chk_third(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
LogData *ld = ptr;
|
||||||
|
TestLogEntry *e = ld->second.next->next;
|
||||||
|
chk_str_eq(
|
||||||
|
s,
|
||||||
|
"text",
|
||||||
|
e->text,
|
||||||
|
"potential memory leak in test log"
|
||||||
|
);
|
||||||
|
chk_ptr_eq(s, "next", e->next, ld->state.last_log);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mk_log_data(LogData *ld)
|
||||||
|
{
|
||||||
|
mk_sample_state(&ld->state);
|
||||||
|
ld->state.first_log = &ld->first;
|
||||||
|
ld->state.last_log = &ld->second;
|
||||||
|
ld->first.text = "foo";
|
||||||
|
ld->first.next = &ld->second;
|
||||||
|
ld->second.text = "bar";
|
||||||
|
ld->second.next = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chk_ptr_chg(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
const void *actual,
|
||||||
|
const void *prohibited
|
||||||
|
)
|
||||||
|
{
|
||||||
|
test_context_compare(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
chk_ptr_chg_test,
|
||||||
|
actual,
|
||||||
|
prohibited
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
chk_ptr_chg_test(
|
||||||
|
TestState *s,
|
||||||
|
void *actual,
|
||||||
|
void *prohibited
|
||||||
|
)
|
||||||
|
{
|
||||||
|
chk_ptr_ne(s, "has changed", actual, prohibited);
|
||||||
|
chk_ptr_ne(s, "not null", actual, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clean_entry(TestLogEntry *e)
|
||||||
|
{
|
||||||
|
free(e->text);
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
//jl
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void test_append_test_log(TestState *);
|
||||||
|
|
||||||
|
//jl
|
||||||
+13
-85
@@ -26,95 +26,23 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "initial-state.h"
|
#include "initial-state.h"
|
||||||
|
|
||||||
// Internal Prototypes
|
|
||||||
|
|
||||||
static void tests(TestState *);
|
|
||||||
decl_test(initial_test_count);
|
|
||||||
decl_test(initial_pass_count);
|
|
||||||
decl_test(initial_fail_count);
|
|
||||||
decl_test(initial_pend_count);
|
|
||||||
decl_test(initial_first_log);
|
|
||||||
decl_test(initial_last_log);
|
|
||||||
decl_test(initial_ptr);
|
|
||||||
|
|
||||||
// Public Functions
|
// Public Functions
|
||||||
|
|
||||||
void
|
void
|
||||||
test_initial_state(TestState *s)
|
test_initial_state(TestState *s, void *ptr)
|
||||||
{
|
{
|
||||||
print("initial state\n");
|
TestState *scpy = ptr;
|
||||||
run_test(s, initial_test_count);
|
chk_int_eq(s, "run", scpy->run, 0);
|
||||||
run_test(s, initial_pass_count);
|
chk_int_eq(s, "passsed", scpy->passed, 0);
|
||||||
run_test(s, initial_fail_count);
|
chk_int_eq(s, "failed", scpy->failed, 0);
|
||||||
run_test(s, initial_pend_count);
|
chk_int_eq(s, "pending", scpy->pending, 0);
|
||||||
run_test(s, initial_first_log);
|
chk_ptr_eq(s, "first_log", scpy->first_log, 0);
|
||||||
run_test(s, initial_last_log);
|
chk_ptr_eq(s, "last_log", scpy->last_log, 0);
|
||||||
run_test(s, initial_ptr);
|
chk_ptr_eq(s, "ptr", scpy->ptr, 0);
|
||||||
}
|
chk_ptr_eq(s, "context", scpy->context, 0);
|
||||||
|
chk_ptr_eq(s, "full_context", scpy->full_context, 0);
|
||||||
// Internal Functions
|
chk_int_eq(s, "depth", scpy->depth, 0);
|
||||||
|
chk_ptr_ne(s, "report()", scpy->report, 0);
|
||||||
def_test(initial_test_count, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\trun\n");
|
|
||||||
if (scpy->run == 0) return test_success;
|
|
||||||
append_test_log(s, "initial run was nonzero");
|
|
||||||
return test_failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(initial_pass_count, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\tpassed\n");
|
|
||||||
if (scpy->passed == 0) return test_success;
|
|
||||||
append_test_log(s, "initial passed was nonzero");
|
|
||||||
return test_failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(initial_fail_count, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\tfailed\n");
|
|
||||||
if (scpy->failed == 0) return test_success;
|
|
||||||
append_test_log(s, "initial failed was nonzero");
|
|
||||||
return test_failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(initial_pend_count, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\tpending\n");
|
|
||||||
if (scpy->pending == 0) return test_success;
|
|
||||||
append_test_log(s, "initial pending was nonzero");
|
|
||||||
return test_failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(initial_first_log, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\tfirst_log\n");
|
|
||||||
if (scpy->first_log == 0) return test_success;
|
|
||||||
append_test_log(s, "initial first_log was not null");
|
|
||||||
return test_failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(initial_last_log, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\tlast_log\n");
|
|
||||||
if (scpy->last_log == 0) return test_success;
|
|
||||||
append_test_log(s, "initial last_log was not null");
|
|
||||||
return test_failure;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(initial_ptr, s)
|
|
||||||
{
|
|
||||||
TestState *scpy = s->ptr;
|
|
||||||
print("\tptr\n");
|
|
||||||
if (scpy->ptr == 0) return test_success;
|
|
||||||
append_test_log(s, "initial ptr was not null");
|
|
||||||
return test_failure;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
@@ -19,6 +19,6 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void test_initial_state(TestState *);
|
void test_initial_state(TestState *, void *);
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
+19
-2
@@ -17,8 +17,25 @@
|
|||||||
|
|
||||||
</$objtype/mkfile
|
</$objtype/mkfile
|
||||||
|
|
||||||
HFILES=../9unit.h util.h initial-state.h run-test.h
|
HFILES=\
|
||||||
OFILES=util.$O initial-state.$O run-test.$O
|
../9unit.h\
|
||||||
|
util.h\
|
||||||
|
initial-state.h\
|
||||||
|
run-test.h\
|
||||||
|
run-test-with.h\
|
||||||
|
run-test-compare.h\
|
||||||
|
append-test-log.h\
|
||||||
|
test-context.h
|
||||||
|
|
||||||
|
OFILES=\
|
||||||
|
util.$O\
|
||||||
|
initial-state.$O\
|
||||||
|
run-test.$O\
|
||||||
|
run-test-with.$O\
|
||||||
|
run-test-compare.$O\
|
||||||
|
append-test-log.$O\
|
||||||
|
test-context.$O
|
||||||
|
|
||||||
LIB=../9unit.a
|
LIB=../9unit.a
|
||||||
TEST=tests
|
TEST=tests
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
|
||||||
|
#include "../9unit.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "run-test-compare.h"
|
||||||
|
|
||||||
|
// Internal Prototypes
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
test_run_test_compare_test(TestState *, void *, void *);
|
||||||
|
|
||||||
|
// Public Functions
|
||||||
|
|
||||||
|
void
|
||||||
|
test_run_test_compare(TestState *s)
|
||||||
|
{
|
||||||
|
TestState actual, expected;
|
||||||
|
|
||||||
|
// build initial state
|
||||||
|
mk_sample_state(&actual);
|
||||||
|
void
|
||||||
|
*pass_in1 = malloc(1),
|
||||||
|
*pass_in2 = malloc(1),
|
||||||
|
*passed_in[2];
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
passed_in[i] = 0;
|
||||||
|
actual.ptr = passed_in;
|
||||||
|
|
||||||
|
// run it and see what was passed
|
||||||
|
run_test_compare(
|
||||||
|
&actual,
|
||||||
|
test_run_test_compare_test,
|
||||||
|
pass_in1,
|
||||||
|
pass_in2
|
||||||
|
);
|
||||||
|
chk_ptr_eq(s, "first value passed", passed_in[0], pass_in1);
|
||||||
|
chk_ptr_eq(s, "second value passed", passed_in[1], pass_in2);
|
||||||
|
|
||||||
|
// ensure the state was updated correctly
|
||||||
|
mk_sample_state(&expected);
|
||||||
|
expected.passed = 2;
|
||||||
|
expected.run = 7;
|
||||||
|
expected.ptr = &passed_in;
|
||||||
|
chk_TestState_eq(s, "resulting state", &actual, &expected);
|
||||||
|
|
||||||
|
free(pass_in1);
|
||||||
|
free(pass_in2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Functions
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
test_run_test_compare_test(
|
||||||
|
TestState *s,
|
||||||
|
void *ptr1,
|
||||||
|
void *ptr2
|
||||||
|
)
|
||||||
|
{
|
||||||
|
void **passed_in = s->ptr;
|
||||||
|
passed_in[0] = ptr1;
|
||||||
|
passed_in[1] = ptr2;
|
||||||
|
return test_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//jl
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void test_run_test_compare(TestState *);
|
||||||
|
|
||||||
|
//jl
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
|
||||||
|
#include "../9unit.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "run-test-with.h"
|
||||||
|
|
||||||
|
// Internal Prototypes
|
||||||
|
|
||||||
|
static TestResult test_run_test_with_test(TestState *, void *);
|
||||||
|
|
||||||
|
// Public Functions
|
||||||
|
|
||||||
|
void
|
||||||
|
test_run_test_with(TestState *s)
|
||||||
|
{
|
||||||
|
TestState actual, expected;
|
||||||
|
|
||||||
|
// build initial state
|
||||||
|
mk_sample_state(&actual);
|
||||||
|
void
|
||||||
|
*pass_in = malloc(0),
|
||||||
|
*passed_in = 0;
|
||||||
|
actual.ptr = &passed_in;
|
||||||
|
|
||||||
|
// run it and see what was passed
|
||||||
|
run_test_with(&actual, test_run_test_with_test, pass_in);
|
||||||
|
chk_ptr_eq(s, "value passed", passed_in, pass_in);
|
||||||
|
|
||||||
|
// ensure the state was updated correctly
|
||||||
|
mk_sample_state(&expected);
|
||||||
|
expected.passed = 2;
|
||||||
|
expected.run = 7;
|
||||||
|
expected.ptr = &passed_in;
|
||||||
|
chk_TestState_eq(s, "resulting state", &actual, &expected);
|
||||||
|
|
||||||
|
free(pass_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Functions
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
test_run_test_with_test(TestState *s, void *ptr)
|
||||||
|
{
|
||||||
|
void **passed_in = s->ptr;
|
||||||
|
*passed_in = ptr;
|
||||||
|
return test_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
//jl
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void test_run_test_with(TestState *);
|
||||||
|
|
||||||
|
//jl
|
||||||
+58
-65
@@ -31,20 +31,22 @@
|
|||||||
static void test_pass(TestState *);
|
static void test_pass(TestState *);
|
||||||
static void test_fail(TestState *);
|
static void test_fail(TestState *);
|
||||||
static void test_pend(TestState *);
|
static void test_pend(TestState *);
|
||||||
static void mk_sample_state(TestState *);
|
static TestResult null_state(TestState *);
|
||||||
decl_test(always_passes);
|
static void null_test(TestState *);
|
||||||
decl_test(always_fails);
|
static TestResult always_passes(TestState *);
|
||||||
decl_test(always_pends);
|
static TestResult always_fails(TestState *);
|
||||||
|
static TestResult always_pends(TestState *);
|
||||||
|
|
||||||
// Public Functions
|
// Public Functions
|
||||||
|
|
||||||
void
|
void
|
||||||
test_run_test(TestState *s)
|
test_run_test(TestState *s)
|
||||||
{
|
{
|
||||||
print("run_test()\n");
|
test_context(s, "passing", test_pass);
|
||||||
test_pass(s);
|
test_context(s, "failing", test_fail);
|
||||||
test_fail(s);
|
test_context(s, "pending", test_pend);
|
||||||
test_pend(s);
|
single_test_context(s, "null state", null_state);
|
||||||
|
test_context(s, "null test", null_test);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal Functions
|
// Internal Functions
|
||||||
@@ -52,102 +54,93 @@ test_run_test(TestState *s)
|
|||||||
static void
|
static void
|
||||||
test_pass(TestState *s)
|
test_pass(TestState *s)
|
||||||
{
|
{
|
||||||
TestState expected, actual;
|
TestState actual, expected;
|
||||||
print("\tpassing\n");
|
|
||||||
|
|
||||||
// expected result
|
|
||||||
memset(&expected, 0, sizeof(TestState));
|
|
||||||
expected.passed = 2;
|
|
||||||
expected.failed = 2;
|
|
||||||
expected.pending = 3;
|
|
||||||
expected.run = 7;
|
|
||||||
|
|
||||||
// actual result
|
// actual result
|
||||||
mk_sample_state(&actual);
|
mk_sample_state(&actual);
|
||||||
run_test(&actual, always_passes);
|
run_test(&actual, always_passes);
|
||||||
|
|
||||||
compare_states(
|
// expected result
|
||||||
s,
|
mk_sample_state(&expected);
|
||||||
"\t\t",
|
expected.passed = 2;
|
||||||
"ERROR: run_test(): passing:",
|
expected.run = 7;
|
||||||
&expected,
|
|
||||||
&actual
|
chk_TestState_eq(s, 0, &actual, &expected);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_fail(TestState *s)
|
test_fail(TestState *s)
|
||||||
{
|
{
|
||||||
TestState expected, actual;
|
TestState actual, expected;
|
||||||
print("\tfailing\n");
|
|
||||||
|
|
||||||
// expected result
|
|
||||||
memset(&expected, 0, sizeof(TestState));
|
|
||||||
expected.passed = 1;
|
|
||||||
expected.failed = 3;
|
|
||||||
expected.pending = 3;
|
|
||||||
expected.run = 7;
|
|
||||||
|
|
||||||
// actual result
|
// actual result
|
||||||
mk_sample_state(&actual);
|
mk_sample_state(&actual);
|
||||||
run_test(&actual, always_fails);
|
run_test(&actual, always_fails);
|
||||||
|
|
||||||
compare_states(
|
// expected result
|
||||||
s,
|
mk_sample_state(&expected);
|
||||||
"\t\t",
|
expected.failed = 3;
|
||||||
"ERROR: run_test(): failing:",
|
expected.run = 7;
|
||||||
&expected,
|
|
||||||
&actual
|
chk_TestState_eq(s, 0, &actual, &expected);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_pend(TestState *s)
|
test_pend(TestState *s)
|
||||||
{
|
{
|
||||||
TestState expected, actual;
|
TestState actual, expected;
|
||||||
print("\tpending\n");
|
|
||||||
|
|
||||||
// expected result
|
|
||||||
memset(&expected, 0, sizeof(TestState));
|
|
||||||
expected.passed = 1;
|
|
||||||
expected.failed = 2;
|
|
||||||
expected.pending = 4;
|
|
||||||
expected.run = 7;
|
|
||||||
|
|
||||||
// actual result
|
// actual result
|
||||||
mk_sample_state(&actual);
|
mk_sample_state(&actual);
|
||||||
run_test(&actual, always_pends);
|
run_test(&actual, always_pends);
|
||||||
|
|
||||||
compare_states(
|
// expected result
|
||||||
s,
|
mk_sample_state(&expected);
|
||||||
"\t\t",
|
expected.pending = 4;
|
||||||
"ERROR: run_test(): pending:",
|
expected.run = 7;
|
||||||
&expected,
|
|
||||||
&actual
|
chk_TestState_eq(s, 0, &actual, &expected);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
null_state(TestState *)
|
||||||
|
{
|
||||||
|
// make sure it doesn't crash
|
||||||
|
run_test(0, always_passes);
|
||||||
|
return test_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mk_sample_state(TestState *s)
|
null_test(TestState *s)
|
||||||
{
|
{
|
||||||
memset(s, 0, sizeof(TestState));
|
TestState actual, expected;
|
||||||
s->passed = 1;
|
|
||||||
s->failed = 2;
|
// actual value
|
||||||
s->pending = 3;
|
mk_sample_state(&actual);
|
||||||
s->run = 6;
|
run_test(&actual, 0);
|
||||||
|
|
||||||
|
// expected value
|
||||||
|
mk_sample_state(&expected);
|
||||||
|
expected.pending = 4;
|
||||||
|
expected.run = 7;
|
||||||
|
|
||||||
|
chk_TestState_eq(s, 0, &actual, &expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
decl_test(always_passes)
|
static TestResult
|
||||||
|
always_passes(TestState *)
|
||||||
{
|
{
|
||||||
return test_success;
|
return test_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
decl_test(always_fails)
|
static TestResult
|
||||||
|
always_fails(TestState *)
|
||||||
{
|
{
|
||||||
return test_failure;
|
return test_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
decl_test(always_pends)
|
static TestResult
|
||||||
|
always_pends(TestState *)
|
||||||
{
|
{
|
||||||
return test_pending;
|
return test_pending;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <u.h>
|
||||||
|
#include <libc.h>
|
||||||
|
|
||||||
|
#include "../9unit.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "test-context.h"
|
||||||
|
|
||||||
|
// Internal Types
|
||||||
|
|
||||||
|
typedef struct ContextData ContextData;
|
||||||
|
|
||||||
|
struct ContextData
|
||||||
|
{
|
||||||
|
TestState state;
|
||||||
|
const char *initial_context;
|
||||||
|
const char *initial_full_context;
|
||||||
|
int initial_depth;
|
||||||
|
const char *new_context;
|
||||||
|
const char *expected_sub_full_context;
|
||||||
|
int expected_sub_depth;
|
||||||
|
const char *sub_context;
|
||||||
|
const char *sub_full_context;
|
||||||
|
int sub_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Internal Prototypes
|
||||||
|
|
||||||
|
static void no_prior_context(TestState *);
|
||||||
|
static void prior_context(TestState *);
|
||||||
|
static void test_context_common(TestState *, ContextData *);
|
||||||
|
static void init_ContextData(ContextData *);
|
||||||
|
static void get_context(TestState *);
|
||||||
|
static void check_results(TestState *, ContextData *);
|
||||||
|
static void clean_up(ContextData *);
|
||||||
|
|
||||||
|
// Public Functions
|
||||||
|
|
||||||
|
void
|
||||||
|
test_test_context(TestState *s)
|
||||||
|
{
|
||||||
|
test_context(s, "no prior context", no_prior_context);
|
||||||
|
test_context(s, "prior context", prior_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Functions
|
||||||
|
|
||||||
|
static void
|
||||||
|
no_prior_context(TestState *s)
|
||||||
|
{
|
||||||
|
ContextData cd;
|
||||||
|
|
||||||
|
// set initial state
|
||||||
|
cd.new_context = "my test";
|
||||||
|
cd.initial_context = 0;
|
||||||
|
cd.initial_full_context = 0;
|
||||||
|
cd.initial_depth = 0;
|
||||||
|
|
||||||
|
// set expectations
|
||||||
|
cd.expected_sub_full_context = "my test";
|
||||||
|
cd.expected_sub_depth = 1;
|
||||||
|
|
||||||
|
test_context_common(s, &cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
prior_context(TestState *s)
|
||||||
|
{
|
||||||
|
ContextData cd;
|
||||||
|
|
||||||
|
// set initial state
|
||||||
|
cd.new_context = "my other test";
|
||||||
|
cd.initial_context = "bar";
|
||||||
|
cd.initial_full_context = "foo: bar";
|
||||||
|
cd.initial_depth = 2;
|
||||||
|
|
||||||
|
// set expectations
|
||||||
|
cd.expected_sub_full_context = "foo: bar: my other test";
|
||||||
|
cd.expected_sub_depth = 3;
|
||||||
|
|
||||||
|
test_context_common(s, &cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_context_common(
|
||||||
|
TestState *s,
|
||||||
|
ContextData *cd
|
||||||
|
)
|
||||||
|
{
|
||||||
|
init_ContextData(cd);
|
||||||
|
test_context(&cd->state, cd->new_context, get_context);
|
||||||
|
check_results(s, cd);
|
||||||
|
clean_up(cd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_ContextData(ContextData *cd)
|
||||||
|
{
|
||||||
|
mk_sample_state(&cd->state);
|
||||||
|
cd->state.ptr = cd;
|
||||||
|
|
||||||
|
// initial context
|
||||||
|
if (cd->initial_context)
|
||||||
|
{
|
||||||
|
cd->state.context =
|
||||||
|
malloc(strlen(cd->initial_context) + 1);
|
||||||
|
strcpy(cd->state.context, cd->initial_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial full_context
|
||||||
|
if (cd->initial_full_context)
|
||||||
|
{
|
||||||
|
cd->state.full_context =
|
||||||
|
malloc(strlen(cd->initial_full_context) + 1);
|
||||||
|
strcpy(
|
||||||
|
cd->state.full_context,
|
||||||
|
cd->initial_full_context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cd->state.depth = cd->initial_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_context(TestState *s)
|
||||||
|
{
|
||||||
|
ContextData *cd = s->ptr;
|
||||||
|
|
||||||
|
// get the context
|
||||||
|
if (s->context)
|
||||||
|
{
|
||||||
|
char *c = malloc(strlen(s->context) + 1);
|
||||||
|
strcpy(c, s->context);
|
||||||
|
cd->sub_context = c;
|
||||||
|
}
|
||||||
|
else cd->sub_context = 0;
|
||||||
|
|
||||||
|
// get the full context
|
||||||
|
if (s->full_context)
|
||||||
|
{
|
||||||
|
char *fc = malloc(strlen(s->full_context) + 1);
|
||||||
|
strcpy(fc, s->full_context);
|
||||||
|
cd->sub_full_context = fc;
|
||||||
|
}
|
||||||
|
else cd->sub_full_context = 0;
|
||||||
|
|
||||||
|
// get the depth
|
||||||
|
cd->sub_depth = s->depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_results(
|
||||||
|
TestState *s,
|
||||||
|
ContextData *cd
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// sub context
|
||||||
|
chk_str_eq(
|
||||||
|
s,
|
||||||
|
"sub context",
|
||||||
|
cd->sub_context,
|
||||||
|
cd->new_context
|
||||||
|
);
|
||||||
|
|
||||||
|
// sub full_context
|
||||||
|
chk_str_eq(
|
||||||
|
s,
|
||||||
|
"sub full_context",
|
||||||
|
cd->sub_full_context,
|
||||||
|
cd->expected_sub_full_context
|
||||||
|
);
|
||||||
|
|
||||||
|
// sub depth
|
||||||
|
chk_int_eq(
|
||||||
|
s,
|
||||||
|
"sub depth",
|
||||||
|
cd->sub_depth,
|
||||||
|
cd->expected_sub_depth
|
||||||
|
);
|
||||||
|
|
||||||
|
// final context
|
||||||
|
chk_str_eq(
|
||||||
|
s,
|
||||||
|
"final context",
|
||||||
|
cd->state.context,
|
||||||
|
cd->initial_context
|
||||||
|
);
|
||||||
|
|
||||||
|
// final full_context
|
||||||
|
chk_str_eq(
|
||||||
|
s,
|
||||||
|
"final full_context",
|
||||||
|
cd->state.full_context,
|
||||||
|
cd->initial_full_context
|
||||||
|
);
|
||||||
|
|
||||||
|
// final depth
|
||||||
|
chk_int_eq(
|
||||||
|
s,
|
||||||
|
"final depth",
|
||||||
|
cd->state.depth,
|
||||||
|
cd->initial_depth
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clean_up(ContextData *cd)
|
||||||
|
{
|
||||||
|
free(cd->state.context);
|
||||||
|
free(cd->state.full_context);
|
||||||
|
free(cd->sub_context);
|
||||||
|
free(cd->sub_full_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
//jl
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
9unit
|
||||||
|
Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation, either version 3 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this program. If not, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void test_test_context(TestState *);
|
||||||
|
|
||||||
|
//jl
|
||||||
+11
-4
@@ -25,6 +25,10 @@
|
|||||||
#include "../9unit.h"
|
#include "../9unit.h"
|
||||||
#include "initial-state.h"
|
#include "initial-state.h"
|
||||||
#include "run-test.h"
|
#include "run-test.h"
|
||||||
|
#include "run-test-with.h"
|
||||||
|
#include "run-test-compare.h"
|
||||||
|
#include "append-test-log.h"
|
||||||
|
#include "test-context.h"
|
||||||
|
|
||||||
// Internal Prototypes
|
// Internal Prototypes
|
||||||
|
|
||||||
@@ -46,13 +50,16 @@ tests(TestState *s)
|
|||||||
{
|
{
|
||||||
if (!s) exits("ERROR: no TestState");
|
if (!s) exits("ERROR: no TestState");
|
||||||
|
|
||||||
// Make a copy of the initial TestState and store it
|
// Make a copy of the initial TestState
|
||||||
TestState scpy;
|
TestState scpy;
|
||||||
memcpy(&scpy, s, sizeof(TestState));
|
memcpy(&scpy, s, sizeof(TestState));
|
||||||
s->ptr = &scpy;
|
|
||||||
|
|
||||||
test_initial_state(s);
|
test_context_with(s, "initial state", test_initial_state, &scpy);
|
||||||
test_run_test(s);
|
test_context(s, "run_test()", test_run_test);
|
||||||
|
test_context(s, "run_test_with()", test_run_test_with);
|
||||||
|
test_context(s, "run_test_compare()", test_run_test_compare);
|
||||||
|
test_context(s, "append_test_log()", test_append_test_log);
|
||||||
|
test_context(s, "test_context()", test_test_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
+219
-262
@@ -26,303 +26,260 @@
|
|||||||
#include "../9unit.h"
|
#include "../9unit.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
// Local Types
|
// Internal Prototypes
|
||||||
|
|
||||||
typedef struct CompareInts CompareInts;
|
static void chk_TestState_eq_test(TestState *, void *, void *);
|
||||||
typedef struct ComparePtrs ComparePtrs;
|
static TestResult chk_int_eq_test(TestState *, void *, void *);
|
||||||
|
static TestResult chk_ptr_eq_test(TestState *, void *, void *);
|
||||||
|
static TestResult chk_ptr_ne_test(TestState *, void *, void *);
|
||||||
|
static TestResult chk_str_eq_test(TestState *, void *, void *);
|
||||||
|
|
||||||
struct CompareInts
|
// discard report data
|
||||||
{
|
static void report(const char *);
|
||||||
const char *context;
|
|
||||||
int expected;
|
|
||||||
int actual;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ComparePtrs
|
|
||||||
{
|
|
||||||
const char *context;
|
|
||||||
const void *expected;
|
|
||||||
const void *actual;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Local Prototypes
|
|
||||||
|
|
||||||
// compare the run value of two states
|
|
||||||
static void compare_run(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare the passed value of two states
|
|
||||||
static void compare_passed(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare the failed value of two states
|
|
||||||
static void compare_failed(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare the pending value of two states
|
|
||||||
static void compare_pending(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare the first_log value of two states
|
|
||||||
static void compare_first_log(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare the last_log value of two states
|
|
||||||
static void compare_last_log(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare the ptr value of two states
|
|
||||||
static void compare_ptr(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
const char *,
|
|
||||||
const TestState *,
|
|
||||||
const TestState *
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare two ints
|
|
||||||
static void compare_ints(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
int,
|
|
||||||
int
|
|
||||||
);
|
|
||||||
|
|
||||||
// compare two pointers
|
|
||||||
static void compare_ptrs(
|
|
||||||
TestState *,
|
|
||||||
const char *,
|
|
||||||
void *,
|
|
||||||
void *
|
|
||||||
);
|
|
||||||
|
|
||||||
decl_test(compare_ints_test);
|
|
||||||
decl_test(compare_ptrs_test);
|
|
||||||
|
|
||||||
// Public Functions
|
// Public Functions
|
||||||
|
|
||||||
void
|
void
|
||||||
compare_states(
|
mk_sample_state(TestState *s)
|
||||||
|
{
|
||||||
|
memset(s, 0, sizeof(TestState));
|
||||||
|
s->passed = 1;
|
||||||
|
s->failed = 2;
|
||||||
|
s->pending = 3;
|
||||||
|
s->run = 6;
|
||||||
|
s->report = report;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
chk_TestState_eq(
|
||||||
TestState *s,
|
TestState *s,
|
||||||
const char *prefix,
|
|
||||||
const char *context,
|
const char *context,
|
||||||
const TestState *expected,
|
const TestState *actual,
|
||||||
const TestState *actual
|
const TestState *expected
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
compare_run(s, prefix, context, expected, actual);
|
test_context_compare(
|
||||||
compare_passed(s, prefix, context, expected, actual);
|
s,
|
||||||
compare_failed(s, prefix, context, expected, actual);
|
context,
|
||||||
compare_pending(s, prefix, context, expected, actual);
|
chk_TestState_eq_test,
|
||||||
compare_first_log(s, prefix, context, expected, actual);
|
actual,
|
||||||
compare_last_log(s, prefix, context, expected, actual);
|
expected
|
||||||
compare_ptr(s, prefix, context, expected, actual);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
chk_int_eq(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
int actual,
|
||||||
|
int expected
|
||||||
|
)
|
||||||
|
{
|
||||||
|
single_test_context_compare(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
chk_int_eq_test,
|
||||||
|
&actual,
|
||||||
|
&expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
chk_ptr_eq(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
const void *actual,
|
||||||
|
const void *expected
|
||||||
|
)
|
||||||
|
{
|
||||||
|
single_test_context_compare(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
chk_ptr_eq_test,
|
||||||
|
actual,
|
||||||
|
expected
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
chk_ptr_ne(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
const void *actual,
|
||||||
|
const void *prohibited
|
||||||
|
)
|
||||||
|
{
|
||||||
|
single_test_context_compare(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
chk_ptr_ne_test,
|
||||||
|
actual,
|
||||||
|
prohibited
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
chk_str_eq(
|
||||||
|
TestState *s,
|
||||||
|
const char *context,
|
||||||
|
const char *actual,
|
||||||
|
const char *expected
|
||||||
|
)
|
||||||
|
{
|
||||||
|
single_test_context_compare(
|
||||||
|
s,
|
||||||
|
context,
|
||||||
|
chk_str_eq_test,
|
||||||
|
actual,
|
||||||
|
expected
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local Functions
|
// Local Functions
|
||||||
|
|
||||||
static void
|
static void
|
||||||
compare_run(
|
chk_TestState_eq_test(
|
||||||
TestState *s,
|
TestState *s,
|
||||||
const char *prefix,
|
void *aptr,
|
||||||
const char *context,
|
void *eptr
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
char full_context[STR_BUF_SIZE];
|
if (!eptr)
|
||||||
print(prefix);
|
{
|
||||||
print("run\n");
|
chk_ptr_eq(s, "is null", aptr, 0);
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s run:", context);
|
return;
|
||||||
compare_ints(s, full_context, expected->run, actual->run);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
TestState
|
||||||
compare_passed(
|
*actual = aptr,
|
||||||
|
*expected = eptr;
|
||||||
|
|
||||||
|
chk_int_eq(s, "run", actual->run, expected->run);
|
||||||
|
chk_int_eq(s, "passed", actual->passed, expected->passed);
|
||||||
|
chk_int_eq(s, "failed", actual->failed, expected->failed);
|
||||||
|
chk_int_eq(s, "pending", actual->pending, expected->pending);
|
||||||
|
chk_ptr_eq(s, "first_log", actual->first_log, expected->first_log);
|
||||||
|
chk_ptr_eq(s, "last_log", actual->last_log, expected->last_log);
|
||||||
|
chk_ptr_eq(s, "ptr", actual->ptr, expected->ptr);
|
||||||
|
chk_str_eq(s, "context", actual->context, expected->context);
|
||||||
|
chk_str_eq(s, "full_context", actual->full_context, expected->full_context);
|
||||||
|
chk_int_eq(s, "depth", actual->depth, expected->depth);
|
||||||
|
chk_ptr_eq(s, "report()", actual->report, expected->report);
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
chk_int_eq_test(
|
||||||
TestState *s,
|
TestState *s,
|
||||||
const char *prefix,
|
void *aptr,
|
||||||
const char *context,
|
void *eptr
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
char full_context[STR_BUF_SIZE];
|
int
|
||||||
print(prefix);
|
*actual = aptr,
|
||||||
print("passed\n");
|
*expected = eptr;
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s passed:", context);
|
|
||||||
compare_ints(s, full_context, expected->passed, actual->passed);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
if (*actual == *expected)
|
||||||
compare_failed(
|
return test_success;
|
||||||
TestState *s,
|
|
||||||
const char *prefix,
|
|
||||||
const char *context,
|
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char full_context[STR_BUF_SIZE];
|
|
||||||
print(prefix);
|
|
||||||
print("failed\n");
|
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s failed:", context);
|
|
||||||
compare_ints(s, full_context, expected->failed, actual->failed);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
// log the error
|
||||||
compare_pending(
|
|
||||||
TestState *s,
|
|
||||||
const char *prefix,
|
|
||||||
const char *context,
|
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char full_context[STR_BUF_SIZE];
|
|
||||||
print(prefix);
|
|
||||||
print("pending\n");
|
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s pending:", context);
|
|
||||||
compare_ints(s, full_context, expected->pending, actual->pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
compare_first_log(
|
|
||||||
TestState *s,
|
|
||||||
const char *prefix,
|
|
||||||
const char *context,
|
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char full_context[STR_BUF_SIZE];
|
|
||||||
print(prefix);
|
|
||||||
print("first_log\n");
|
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s first_log:", context);
|
|
||||||
compare_ptrs(s, full_context, expected->first_log, actual->first_log);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
compare_last_log(
|
|
||||||
TestState *s,
|
|
||||||
const char *prefix,
|
|
||||||
const char *context,
|
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char full_context[STR_BUF_SIZE];
|
|
||||||
print(prefix);
|
|
||||||
print("last_log\n");
|
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s last_log:", context);
|
|
||||||
compare_ptrs(s, full_context, expected->last_log, actual->last_log);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
compare_ptr(
|
|
||||||
TestState *s,
|
|
||||||
const char *prefix,
|
|
||||||
const char *context,
|
|
||||||
const TestState *expected,
|
|
||||||
const TestState *actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
char full_context[STR_BUF_SIZE];
|
|
||||||
print(prefix);
|
|
||||||
print("ptr\n");
|
|
||||||
snprintf(full_context, STR_BUF_SIZE, "%s ptr:", context);
|
|
||||||
compare_ptrs(s, full_context, expected->ptr, actual->ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
compare_ints(
|
|
||||||
TestState *s,
|
|
||||||
const char *context,
|
|
||||||
int expected,
|
|
||||||
int actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
void *old_ptr = s->ptr;
|
|
||||||
CompareInts ci;
|
|
||||||
ci.context = context;
|
|
||||||
ci.expected = expected;
|
|
||||||
ci.actual = actual;
|
|
||||||
s->ptr = &ci;
|
|
||||||
run_test(s, compare_ints_test);
|
|
||||||
s->ptr = old_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
compare_ptrs(
|
|
||||||
TestState *s,
|
|
||||||
const char *context,
|
|
||||||
void *expected,
|
|
||||||
void *actual
|
|
||||||
)
|
|
||||||
{
|
|
||||||
void *old_ptr = s->ptr;
|
|
||||||
ComparePtrs cp;
|
|
||||||
cp.context = context;
|
|
||||||
cp.expected = expected;
|
|
||||||
cp.actual = actual;
|
|
||||||
s->ptr = &cp;
|
|
||||||
run_test(s, compare_ptrs_test);
|
|
||||||
s->ptr = old_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
def_test(compare_ints_test, s)
|
|
||||||
{
|
|
||||||
const CompareInts *ci = s->ptr;
|
|
||||||
if (ci->actual == ci->expected) return test_success;
|
|
||||||
char str[STR_BUF_SIZE];
|
char str[STR_BUF_SIZE];
|
||||||
append_test_log(s, ci->context);
|
log_test_context(s);
|
||||||
snprintf(str, STR_BUF_SIZE, "\texpected: %d", ci->expected);
|
append_test_log(s, "ERROR:");
|
||||||
|
snprintf(str, STR_BUF_SIZE, "\texpected: %d", *expected);
|
||||||
append_test_log(s, str);
|
append_test_log(s, str);
|
||||||
snprintf(str, STR_BUF_SIZE, "\tactual: %d", ci->actual);
|
snprintf(str, STR_BUF_SIZE, "\tactual: %d", *actual);
|
||||||
|
append_test_log(s, str);
|
||||||
|
|
||||||
|
return test_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
chk_ptr_eq_test(
|
||||||
|
TestState *s,
|
||||||
|
void *actual,
|
||||||
|
void *expected
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (actual == expected)
|
||||||
|
return test_success;
|
||||||
|
|
||||||
|
// log the error
|
||||||
|
char str[STR_BUF_SIZE];
|
||||||
|
log_test_context(s);
|
||||||
|
append_test_log(s, "ERROR:");
|
||||||
|
snprintf(str, STR_BUF_SIZE, "\texpected: 0x%x", expected);
|
||||||
|
append_test_log(s, str);
|
||||||
|
snprintf(str, STR_BUF_SIZE, "\tactual: 0x%x", actual);
|
||||||
|
append_test_log(s, str);
|
||||||
|
|
||||||
|
return test_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
chk_ptr_ne_test(
|
||||||
|
TestState *s,
|
||||||
|
void *actual,
|
||||||
|
void *prohibited
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (actual != prohibited)
|
||||||
|
return test_success;
|
||||||
|
|
||||||
|
// log the error
|
||||||
|
char str[STR_BUF_SIZE];
|
||||||
|
log_test_context(s);
|
||||||
|
snprintf(
|
||||||
|
str,
|
||||||
|
STR_BUF_SIZE,
|
||||||
|
"ERROR: prohibited value: 0x%x",
|
||||||
|
actual
|
||||||
|
);
|
||||||
|
append_test_log(s, str);
|
||||||
|
|
||||||
|
return test_failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestResult
|
||||||
|
chk_str_eq_test(
|
||||||
|
TestState *s,
|
||||||
|
void *aptr,
|
||||||
|
void *eptr
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!eptr)
|
||||||
|
{
|
||||||
|
if (!aptr) return test_success;
|
||||||
|
char str[STR_BUF_SIZE];
|
||||||
|
log_test_context(s);
|
||||||
|
append_test_log(s, "ERROR:");
|
||||||
|
append_test_log(s, "\texpected: 0x0");
|
||||||
|
snprintf(str, STR_BUF_SIZE, "\tactual: 0x%x", aptr);
|
||||||
append_test_log(s, str);
|
append_test_log(s, str);
|
||||||
return test_failure;
|
return test_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
def_test(compare_ptrs_test, s)
|
const char
|
||||||
{
|
*actual = aptr,
|
||||||
const ComparePtrs *cp = s->ptr;
|
*expected = eptr;
|
||||||
if (cp->actual == cp->expected) return test_success;
|
|
||||||
|
if (!strcmp(actual, expected)) return;
|
||||||
|
|
||||||
|
// log the error
|
||||||
char str[STR_BUF_SIZE];
|
char str[STR_BUF_SIZE];
|
||||||
append_test_log(s, cp->context);
|
log_test_context(s);
|
||||||
snprintf(str, STR_BUF_SIZE, "\texpected: 0x%x", cp->expected);
|
append_test_log(s, "ERROR:");
|
||||||
|
snprintf(str, STR_BUF_SIZE, "\texpected: %s", expected);
|
||||||
append_test_log(s, str);
|
append_test_log(s, str);
|
||||||
snprintf(str, STR_BUF_SIZE, "\tactual: 0x%x", cp->actual);
|
snprintf(str, STR_BUF_SIZE, "\tactual: %s", actual);
|
||||||
append_test_log(s, str);
|
append_test_log(s, str);
|
||||||
|
|
||||||
return test_failure;
|
return test_failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
report(const char *)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
+40
-9
@@ -19,18 +19,49 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define STR_BUF_SIZE 256 // buffer size for constructing arbitrary strings
|
#define STR_BUF_SIZE 256 // maximum string buffer size
|
||||||
|
|
||||||
#define decl_test(n) static TestResult n(TestState *)
|
// initializes a sample TestState value
|
||||||
#define def_test(n, s) static TestResult n(TestState *s)
|
void mk_sample_state(TestState *);
|
||||||
|
|
||||||
// compares two TestState values
|
// ensures two TestState values are equal
|
||||||
extern void compare_states(
|
extern void chk_TestState_eq(
|
||||||
TestState *, // the state we are *actually* updating ;)
|
TestState *, // the state we are *actually* updating ;)
|
||||||
const char *, // prefix for each status line
|
const char *, // the context
|
||||||
const char *, // context for errors
|
const TestState *, // actual state
|
||||||
const TestState *, // expected state
|
const TestState * // expected state
|
||||||
const TestState * // actual state
|
);
|
||||||
|
|
||||||
|
// ensure two integers are equal
|
||||||
|
extern void chk_int_eq(
|
||||||
|
TestState *, // the test state
|
||||||
|
const char *, // the context
|
||||||
|
int, // the actual value
|
||||||
|
int // the expected value
|
||||||
|
);
|
||||||
|
|
||||||
|
// ensure two pointers are equal
|
||||||
|
extern void chk_ptr_eq(
|
||||||
|
TestState *, // the test state
|
||||||
|
const char *, // the context
|
||||||
|
void *, // the actual value
|
||||||
|
void * // the expected value
|
||||||
|
);
|
||||||
|
|
||||||
|
// ensure two pointers are not equal
|
||||||
|
extern void chk_ptr_ne(
|
||||||
|
TestState *, // the test state
|
||||||
|
const char *, // the context
|
||||||
|
void *, // the actual value
|
||||||
|
void * // the prohibited value
|
||||||
|
);
|
||||||
|
|
||||||
|
// ensure two strings are equal
|
||||||
|
extern void chk_str_eq(
|
||||||
|
TestState *, // the test state
|
||||||
|
const char *, // the context
|
||||||
|
const char *, // the actual value
|
||||||
|
const char * // the expected value
|
||||||
);
|
);
|
||||||
|
|
||||||
//jl
|
//jl
|
||||||
|
|||||||
Reference in New Issue
Block a user