# mcandre/qc

restored to working version

mcandre committed Sep 26, 2011
1 parent 05634d0 commit 4b220af8221cc3057549d927b4e3b59b48325543
Showing with 105 additions and 48 deletions.
1. +17 −14 example.c
2. +80 −26 qc.c
3. +8 −8 qc.h
 @@ -4,14 +4,15 @@ #include #include -int gen_odd() { - int i = gen_int(); +void gen_odd(blob data) { + int i; + gen_int(&i); if (i % 2 == 0) { i++; } - return i; + qc_return(int, i); } bool is_odd(blob data) { @@ -28,22 +29,24 @@ bool both_less_than(blob data) { return a < c && b < c; } -char gen_digit_char() { - int i = gen_int(); +void gen_digit_char(blob data) { + int i; + gen_int(&i); i %= 10; char c = '0' + (char) i; - return c; + qc_return(char, c); } -int gen_digit() { - int i = gen_int(); +void gen_digit(blob data) { + int i; + gen_int(&i); i %= 10; - return i; + qc_return(int, i); } bool does_not_parse_to(blob data) { @@ -62,32 +65,32 @@ bool does_not_have_an_h(blob data) { int main() { qc_init(); - gen gs[] = { (gen) gen_odd }; + gen gs[] = { gen_odd }; print ps[] = { print_int }; // Are all odd numbers odd? for_all(is_odd, 1, gs, ps, int); - gen gs2[] = { (gen) gen_int }; + gen gs2[] = { gen_int }; print ps2[] = { print_int }; // Are all integers odd? for_all(is_odd, 1, gs2, ps2, int); - gen gs3[] = { (gen) gen_int, (gen) gen_int, (gen) gen_int }; + gen gs3[] = { gen_int, gen_int, gen_int }; print ps3[] = { print_int, print_int, print_int }; // Are any two integers less than a third integer? for_all(both_less_than, 3, gs3, ps3, int); - gen gs4[] = { (gen) gen_digit_char, (gen) gen_digit }; + gen gs4[] = { gen_digit_char, gen_digit }; print ps4[] = { print_char, print_int }; // Do any characters parse to a matching integer? for_all(does_not_parse_to, 2, gs4, ps4, int); - gen gs5[] = { (gen) gen_string }; + gen gs5[] = { gen_string }; print ps5[] = { print_string }; // Do any string pairs match?
106 qc.c
 @@ -11,45 +11,47 @@ void qc_init() { QC_INITIALIZED = true; } -bool gen_bool() { +void gen_bool(blob data) { bool b = rand() % 2 == 0; - return b; + qc_return(bool, b); } -int gen_int() { +void gen_int(blob data) { int i = rand(); - return i; + qc_return(int, i); } -char gen_char() { +void gen_char(blob data) { char c = (char) (rand() % 128); - return c; + qc_return(char, c); } -blob* _gen_array(gen g, size_t size) { +void _gen_array(blob data, gen g, size_t size) { int len = rand() % 100; - blob* arr = GC_MALLOC(len * size); + blob arr = GC_MALLOC(len * size); int i; for (i = 0; i < len; i++) { - *(arr + i * size) = g(); + g(arr + i * size); } - return arr; + qc_return(blob, arr); } -char* gen_string() { - char* s = (char*) gen_array(gen_char, char); +void gen_string(blob data) { + char* s; - return s; + gen_array(&s, gen_char, char); + + qc_return(char*, s); } void print_bool(blob data) { - bool b = qc_args(bool, 0, bool); + bool b = (* (bool*) data); if (b) { printf("true"); @@ -60,51 +62,103 @@ void print_bool(blob data) { } void print_int(blob data) { - int i = qc_args(int, 0, int); + int i = qc_args(int, 0, sizeof(int)); printf("%d", i); } void print_char(blob data) { - char c = qc_args(char, 0, char); + char c = qc_args(char, 0, sizeof(char)); printf("\'%c\'", c); } void print_string(blob data) { - char* s = qc_args(char*, 0, char*); + char* s = qc_args(char*, 0, sizeof(char*)); printf("%s", s); } +// Syntax: +// +// For simplicity, _for_all is called using the for_all macro (see qc.h). +// +// In this example, for_all is testing whether all integers are odd (they are not). +// +// Every for_all call begins with a property function. For example, is_odd takes +// an int and returns a bool. +// +// bool is_odd(void *data); +// +// In order to handle arbitrarily-typed property functions, qc uses a special +// protocol, qc_args, to pass test values to the property function. +// +// bool is_odd(void *data) { +// int n = qc_args(int, 0, sizeof(int)); +// +// return n % 2 == 1; +// } +// +// qc_args' first argument is a type: int, char, int*, char*, int**, char**, ... +// The second argument is the index (qc internally stores all test cases in a +// single array). +// The final argument is the maximum byte size of the property's input types. For +// example, is_equal(short, int)'s maximum byte size would be sizeof(int). + +// +// for_all's first argument is the name of such a property function. The next +// argument is an array of generator functions. The is_odd property +// function has a single argument, an integer. Thus each test case consists of a +// random integer to be passed to the is_odd property function. More complicated +// property functions (e.g. cmp(int, int)) may have multiple arguments, and +// therefore require multiple generators. +// +// gen gs[] = { gen_int }; +// +// When a test case fails, the values for which the property returns false will be +// printed. For each generator, a corresponding printer function is necessary. +// for_all is designed to test properties of arbitrary numbers of arbitrary types; +// because C has no universal print(some_object) function, the framework user must +// specify printer functions for each generator function. More complicated types, +// such as trees, graphs, and linked lists require the framework user to write +// custom printer functions, but the syntax remains the same. +// +// print ps[] = { print_int }; +// +// Finally, for_all requires the maximum byte size of the types to be passed to the +// property function. This information helps for_all hold all test values in a single array, +// which it passes to the test property. +// +// for_all(is_odd, 1, gs, ps, sizeof(int)); + void _for_all(prop property, int arglen, gen gs[], print ps[], size_t max_size) { - int i, j, k; + int i, j; // Because GC_MALLOC will segfault if GC_INIT() is not called beforehand. if (!QC_INITIALIZED) { printf("*** Error: Run qc_init() before calling for_all().\n"); return; } - blob* test_case = GC_MALLOC(arglen * max_size); + blob values = GC_MALLOC(arglen * max_size); - for (i = 0; i < 1/*00*/; i++) { + for (i = 0; i < 100; i++) { for (j = 0; j < arglen; j++) { - *(test_case + j * max_size) = gs[j](); + gs[j](values + j * max_size); } - bool holds = property(test_case); + bool holds = property(values); - // if (!holds) { + if (!holds) { printf("*** Failed!\n"); - for (k = 0; k < arglen; k++) { - ps[k](test_case + k * max_size); + for (j = 0; j < arglen; j++) { + ps[j](values + j * max_size); printf("\n"); } return; - // } + } } printf("+++ OK, passed 100 tests.\n");
16 qc.h
 @@ -17,24 +17,24 @@ bool QC_INITIALIZED; void qc_init(); -// #define qc_return(type, value) ((* (type*) data) = value) +#define qc_return(type, value) ((* (type*) data) = value) #define qc_args(type, n, max_class) ((* (type*) (data + n * sizeof(max_class)))) typedef void* blob; -typedef blob (*gen)(); +typedef void (*gen)(blob); typedef void (*print)(blob); typedef bool (*prop)(blob); -bool gen_bool(); -int gen_int(); -char gen_char(); +void gen_bool(blob data); +void gen_int(blob data); +void gen_char(blob data); -blob* _gen_array(gen g, size_t size); +void _gen_array(blob data, gen g, size_t size); -#define gen_array(g, class) (_gen_array((gen) g, sizeof(class))) +#define gen_array(data, g, class) (_gen_array(data, (gen) g, sizeof(class))) -char* gen_string(); +void gen_string(blob data); void print_bool(blob data); void print_int(blob data);