From: Michael D. Lowis Date: Mon, 13 Apr 2020 02:55:10 +0000 (-0400) Subject: added atf test framework X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=5c0821885fb40ec255e378873c8a37032b11c07e;p=proto%2Fanvil.git added atf test framework --- diff --git a/anvil.h b/anvil.h index ca33033..fb2685d 100644 --- a/anvil.h +++ b/anvil.h @@ -1,3 +1,6 @@ +#ifndef ANVIL_H +#define ANVIL_H + #include #include #include @@ -71,7 +74,7 @@ typedef struct Client { typedef struct Column { struct Column* next; - int mode, width, nclients; + int width; Client* focused; Client* clients; } Column; @@ -200,3 +203,5 @@ void* ecalloc(size_t n, size_t sz); void xfree(void* p); Atom atom(char* str); void sendmsg(Window win, Atom proto, Atom type); + +#endif \ No newline at end of file diff --git a/atf b/atf new file mode 100755 index 0000000..bd37bd2 --- /dev/null +++ b/atf @@ -0,0 +1,354 @@ +#!/usr/bin/env bash + +runner="runner.c" +declare -a sources +header=$(cat<<-EOF +#include +#include +#include + +#ifdef ATF_TEST + #define main MAIN + #define UNITTEST(name,...) void name(void) +#else + #define UNITTEST(name,...) static inline void name(void) +#endif + +#define CHECK(cond) Assert(cond, #cond, __FILE__, __LINE__) + +typedef struct Value { + struct Value* next; + void (*showfn)(struct Value* val); + long ndata; + long data[]; +} Value; + +void Assert(int cond, char* condstr, char* file, int line); +void* ValueAlloc(void (*showfn)(Value* val), size_t num, size_t sz, void* data); +long RandR(long from, long to); +long Rand(void); + +void ShowLong(Value* val); +void ShowBool(Value* val); +void ShowChar(Value* val); +void ShowString(Value* val); + +long MkLongR(long from, long to); +long MkLong(void); +uint8_t MkU8(void); +uint16_t MkU16(void); +uint32_t MkU32(void); +bool MkBool(void); +char MkAsciiChar(void); +char* MkAsciiStringL(size_t len); +char* MkAsciiString(void); +EOF +) + +# get the list of sources +for i do + case "$i" in + *.c) sources+=("$i") ;; + --header) + printf "%s\n" "$header" + exit 0 + ;; + esac +done + +cat <<-EOF > "$runner" +#include "atf.h" +#undef main +#include +#include +#include +#include +#include +#include +#include +#include + +#define RUN(file, line, name, ntrials) \\ + void name(void); \\ + runtestn(file, line, #name, name, ntrials) + +static char* Curr_File = 0; +static char* Curr_Test = 0; +static char* Curr_Expr = 0; +static unsigned int Curr_Line = 0; +static Value* Curr_Values; +static jmp_buf Jump_Buf; +static unsigned long Total = 0; +static unsigned long Failed = 0; +static uintptr_t Heap_Buf[1024*1024]; +static uintptr_t* Heap_Top = Heap_Buf; +static uintptr_t* Heap_Base = Heap_Buf; +static uintptr_t* Heap_End = Heap_Buf + sizeof(Heap_Buf)/sizeof(uintptr_t) - 1; +enum { + FAIL_ASSERT = 1, + FAIL_OOM = 2 +}; + +/* Basic Runtime Functions + ****************************/ +static int print_results(unsigned int seed) +{ + printf("%lu/%lu tests passed with seed %d\n" , Total - Failed, Total, seed); + return Failed; +} + +static void print_values(void) +{ + Value* values = 0; + while (Curr_Values) + { + Value* val = Curr_Values; + Curr_Values = val->next; + val->next = values; + values = val; + } + + while (values) + { + printf(" -> arg: "); + values->showfn(values); + values = values->next; + } +} + +void* malloc(size_t size) +{ + void* ptr = Heap_Top; + size_t num_words = ((size & ~0x7) + ((size & 7) ? 1 : 0)) / sizeof(uintptr_t) + 1; + *Heap_Top = size; + Heap_Top += num_words; + if (Heap_Top > Heap_End) + { + Heap_Top = Heap_Base; // Reset heap in case printf mallocs. + fprintf(stderr,"%s:%d: MEM %s out of memory\n", Curr_File, Curr_Line, Curr_Test); + longjmp(Jump_Buf, FAIL_OOM); + } + else + { + memset(ptr, 0, size); + } + return ptr; +} + +void* calloc(size_t num, size_t size) +{ + return malloc(num * size); +} + +void* realloc(void* ptr, size_t new_size) +{ + uintptr_t old_size = *((uintptr_t*)ptr - 1); + void* newptr = malloc(new_size); + memcpy(newptr, ptr, old_size); + return newptr; +} + +void free(void* ptr) +{ + (void)ptr; /* simply reset the buffer after each test */ +} + +void Assert(int cond, char* condstr, char* file, int line) +{ + if (!cond) + { + Curr_File = file; + Curr_Line = line; + Curr_Expr = condstr; + longjmp(Jump_Buf, 1); + } +} + +static int runtest(void (*fn)(void)) +{ + Curr_Values = 0; + Heap_Top = Heap_Base; + int code = setjmp(Jump_Buf); + if (code == 0) + { + fn(); + } + return code; +} + +static void runtestn(char* file, int line, char* fnname, void (*fn)(void), int ntrials) +{ + Curr_File = file; + Curr_Line = line; + Curr_Test = fnname; + Total++; + for (int i = 0; i < ntrials; i++) + { + int fail = runtest(fn); + if (fail != 0) + { + Failed++; + if (fail == FAIL_ASSERT) + { + if (ntrials == 1) + { + printf("%s:%d: FAIL for test %s \n -> CHECK( %s )\n", Curr_File, Curr_Line, Curr_Test, Curr_Expr); + } + else + { + printf("%s:%d: FAIL on trial %d/%d for test %s\n", Curr_File, Curr_Line, i, ntrials, Curr_Test); + print_values(); + } + } + break; + } + } +} + +static void handle_signal(int sig) +{ + /* Determine the signal name */ + char* sig_name = NULL; + switch(sig) + { + case SIGABRT: sig_name = "SIGABRT"; break; + case SIGBUS: sig_name = "SIGBUS"; break; + case SIGFPE: sig_name = "SIGFPE"; break; + case SIGILL: sig_name = "SIGILL"; break; + case SIGSEGV: sig_name = "SIGSEGV"; break; + case SIGSYS: sig_name = "SIGSYS"; break; + /* If we don't recognize it then just return and let the default handler + catch it. */ + default: return; + } + /* Error and exit. No summary will be printed but the user will know which + test has crashed. */ + fprintf(stderr,"%s:%d: CRASH %s (signal: %d - %s)\n", Curr_File, Curr_Line, Curr_Test, sig, sig_name); + Failed++; + printf("%lu/%lu tests passed\n" , Total - Failed, Total); + _Exit(1); +} + +/* Property Testing Functions + ****************************/ +void* ValueAlloc(void (*showfn)(Value* val), size_t num, size_t sz, void* data) +{ + Value* value = calloc(num, sizeof(Value) + sz); + value->showfn = showfn; + value->ndata = num; + value->next = Curr_Values; + Curr_Values = value; + if (data) + { + memcpy(value->data, data, num * sz); + } + return &(value->data[0]); +} + +long RandR(long from, long to) +{ + return ((rand() % (to - from + 1)) + from); +} + +long Rand(void) +{ + return rand(); +} + +void ShowLong(Value* val) +{ + printf("%ld\n", (val->data[0])); +} + +void ShowBool(Value* val) +{ + printf("%s\n", (val->data[0] ? "true" : "false")); +} + +void ShowChar(Value* val) +{ + printf("'%c'\n", (char)(val->data[0])); +} + +void ShowString(Value* val) +{ + printf("'%s'\n", (char*)(val->data)); +} + +long MkLongR(long from, long to) +{ + return *((long*)ValueAlloc(ShowLong, 1, sizeof(long), &(long){RandR(from, to)})); +} + +long MkLong(void) +{ + return *((long*)ValueAlloc(ShowLong, 1, sizeof(long), &(long){Rand()})); +} + +uint8_t MkU8(void) +{ + return MkLongR(0, UINT8_MAX); +} + +uint16_t MkU16(void) +{ + return MkLongR(0, UINT16_MAX); +} + +uint32_t MkU32(void) +{ + return MkLongR(0, UINT32_MAX); +} + +bool MkBool(void) +{ + return *((bool*)ValueAlloc(ShowBool, 1, sizeof(bool), &(bool){RandR(0, 1)})); +} + +char MkAsciiChar(void) +{ + return *((char*)ValueAlloc(ShowChar, 1, sizeof(char), &(char){RandR(32, 127)})); +} + +char* MkAsciiStringL(size_t len) { + char* val = ValueAlloc(ShowString, len+1, sizeof(char), 0); + for (size_t i = 0; i < len; i++) + { + *(val++) = RandR(32, 127); + } + return val; +} + +char* MkAsciiString(void) { + return MkAsciiStringL(RandR(1, 1024)); +} + +/* Main Routine + ****************************/ +int main(int argc, char** argv) +{ + (void)runtestn; + unsigned int seed = (argc >= 2 ? strtoul(argv[1], 0, 0) : (unsigned int)time(0)); + srand(seed); + signal(SIGABRT, handle_signal); + signal(SIGBUS, handle_signal); + signal(SIGFPE, handle_signal); + signal(SIGILL, handle_signal); + signal(SIGSEGV, handle_signal); + signal(SIGSYS, handle_signal); + Heap_Base = Heap_Top; +EOF +grep -Hn '\(PROP\|UNIT\)TEST' "${sources[@]}" | sed ' + s/^\(.*\):\(.*\):[\t ]*UNITTEST(\(.*\),\(.*\)).*$/ RUN("\1",\2,\3,\4);/ + s/^\(.*\):\(.*\):[\t ]*UNITTEST(\(.*\)).*$/ RUN("\1",\2,\3,1);/ +' >> "$runner" +cat <<-EOF >> "$runner" + return print_results(seed); +} +EOF + +cc="${CC:-cc}" +"$cc" -DATF_TEST -g -o runner -O0 "$@" "$runner" && ./runner +error="$?" +rm -f runner "$runner" +exit $error diff --git a/build.sh b/build.sh index ab48db1..8e8f4a6 100755 --- a/build.sh +++ b/build.sh @@ -1,10 +1,13 @@ #!/bin/sh -cc -g -Wall -Wextra -Werror -I/usr/X11R6/include -L/usr/X11R6/lib -o anvil *.c -lX11 -lXinerama -lm +cflags="-g -Wall -Wextra -Werror" +incpath="-I/usr/X11R6/include -Itest/" +libpath="-L/usr/X11R6/lib" +libs="-lX11 -lXinerama -lm" + +cc $cflags $incpath $libpath -o anvil ./*.c $libs && ./atf $incpath test/*.c error="$?" -ctags * -grep -n 'TODO' *.[ch]| sed -e 's/: \+/: /' -e 's/\/\* *//g' -e 's/ *\*\/$//' -e 's/TODO: *//' -printf "\a" -if [ $error -ne 0 ]; then - printf "\a" -fi -exit "$error" + +grep -n 'TODO' ./*.[ch]| sed -e 's/: \+/: /' -e 's/\/\* *//g' -e 's/ *\*\/$//' -e 's/TODO: *//' +ctags ./* + +exit $error diff --git a/test/atf.h b/test/atf.h new file mode 100644 index 0000000..5ce11c3 --- /dev/null +++ b/test/atf.h @@ -0,0 +1,39 @@ +#include +#include +#include + +#ifdef ATF_TEST + #define main MAIN + #define UNITTEST(name,...) void name(void) +#else + #define UNITTEST(name,...) static inline void name(void) +#endif + +#define CHECK(cond) Assert(cond, #cond, __FILE__, __LINE__) + +typedef struct Value { + struct Value* next; + void (*showfn)(struct Value* val); + long ndata; + long data[]; +} Value; + +void Assert(int cond, char* condstr, char* file, int line); +void* ValueAlloc(void (*showfn)(Value* val), size_t num, size_t sz, void* data); +long RandR(long from, long to); +long Rand(void); + +void ShowLong(Value* val); +void ShowBool(Value* val); +void ShowChar(Value* val); +void ShowString(Value* val); + +long MkLongR(long from, long to); +long MkLong(void); +uint8_t MkU8(void); +uint16_t MkU16(void); +uint32_t MkU32(void); +bool MkBool(void); +char MkAsciiChar(void); +char* MkAsciiStringL(size_t len); +char* MkAsciiString(void); diff --git a/test/tile.c b/test/tile.c new file mode 100644 index 0000000..f3ea4f2 --- /dev/null +++ b/test/tile.c @@ -0,0 +1,53 @@ +#include "../tile.c" +#include "../list.c" +#include "atf.h" + +XConf X; + +/**** Stub Functions ****/ +void client_adjust(Client* c) +{ + (void)c; +} + +void client_setshade(Client* c, int shade) +{ + (void)c, (void)shade; +} + +void mons_layer(Monitor* mon) +{ + (void)mon; +} + +void mouse_totitle(Client* c) +{ + (void)c; +} + +/**** Stub Functions ****/ + +Monitor TestMon = { + .x = 0, .y = 0, + .w = 100, .h = 100, + .midx = 50, .midy = 50 +}; + +Location TestLoc = { + .monitor = &TestMon, + .workspace = &(Workspace){ + .columns = &(Column){ + .width = 50, + .next = &(Column){ + .width = 50 + } + } + }, + +}; + +/**** Stub Functions ****/ +UNITTEST(Foo) +{ + CHECK(1==9); +} \ No newline at end of file