CC=cc
CPPPATH="-Iinc -I/usr/X11/include -I/usr/X11/include/freetype2 -I/usr/include/freetype2"
-CFLAGS="--std=c99 -Wall -Wextra -Werror"
+CFLAGS="-g --std=c99 -Wall -Wextra -Werror"
export CC CPPPATH CFLAGS
\ No newline at end of file
--- /dev/null
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#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);
--- /dev/null
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xft/Xft.h>
+
+int XSync(Display* display, Bool discard);
+int XMapWindow(Display* display, Window w);
\ No newline at end of file
+++ /dev/null
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-#include <X11/Xft/Xft.h>
-
-void XSetForeground(Display* display, GC gc, unsigned long foreground);
-void XFillRectangle(Display* display, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height);
--- /dev/null
+#include <stdc.h>
+#include <liba.h>
+
+char* ARGV0;
\ No newline at end of file
void win_show(UIWin* win)
{
- XMapWindow(X.display, win->self);
- XSync(X.display, False);
+ (void)win;
+// XMapWindow(X.display, win->self);
+// XSync(X.display, False);
}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+
+typedef struct {
+ const char* name;
+ int ndata;
+ char data[];
+} MockValue_T;
+
+typedef struct {
+ const char* name;
+ MockValue_T* retval;
+ int nvalues;
+ MockValue_T* values[];
+} MockCall_T;
+
+#ifndef MAX_CALLS
+#define MAX_CALLS 256
+#endif
+
+#ifndef MAX_MEMORY
+#define MAX_MEMORY ((1024*1024) / sizeof(int))
+#endif
+
+int Memory[MAX_MEMORY];
+MockCall_T* ExpectedCalls[MAX_CALLS];
+MockCall_T* ActualCalls[MAX_CALLS];
+int* CurrentMem = Memory;
+int CurrentExpected = 0;
+int CurrentCall = 0;
+
+void FailMessage(char* fmt, ...)
+{
+ exit(1);
+}
+
+void* Allocate(int sz)
+{
+ int ncells = (sz / sizeof(int)) + ((sz % sizeof(int)) ? 1 : 0);
+ int* next = CurrentMem + ncells;
+ if (next > (Memory + (sizeof(Memory)/sizeof(Memory[0]))))
+ {
+ FailMessage("memory buffer exhausted");
+ }
+ int* curr = CurrentMem;
+ memset(curr, 0, sizeof(int)*ncells);
+ CurrentMem = next;
+ return curr;
+}
+
+void MockInit(void)
+{
+ CurrentMem = Memory;
+ CurrentExpected = 0;
+ CurrentCall = 0;
+}
+
+MockValue_T* MockValue(const char* name, void* data, int ndata)
+{
+ MockValue_T* val = Allocate(sizeof(MockValue_T) + ndata);
+ val->name = name;
+ val->ndata = ndata;
+ memcpy(val->data, data, ndata);
+ return val;
+}
+
+MockCall_T* MockCall(const char* name, int nargs)
+{
+ MockCall_T* call = Allocate(sizeof(MockCall_T) + (nargs * sizeof(MockValue_T*)));
+ call->name = name;
+ call->nvalues = nargs;
+ return call;
+}
+
+void RegisterCall(MockCall_T* call)
+{
+ if (CurrentExpected >= MAX_CALLS)
+ {
+ FailMessage("exhausted expected call buffer");
+ }
+ ExpectedCalls[CurrentExpected++] = call;
+}
+
+void VerifyCall(MockCall_T* call, void* data)
+{
+ if (CurrentCall >= CurrentExpected)
+ {
+ FailMessage("");
+ }
+ MockCall_T* expect = ExpectedCalls[CurrentCall];
+ if (0 != strcmp(expect->name, call->name))
+ {
+ FailMessage("expected a call to '%s', received a call to '%s' instead", expect->name, call->name);
+ }
+ assert(expect->nvalues == call->nvalues);
+ for (int i = 0; i < call->nvalues; i++)
+ {
+ MockValue_T* earg = expect->values[i];
+ MockValue_T* rarg = call->values[i];
+ assert(earg->ndata == rarg->ndata);
+ if (0 != memcmp(earg->data, rarg->data, earg->ndata))
+ {
+ FailMessage("received unexpected value for argument '%s'", earg->name);
+ }
+ }
+ if (expect->retval)
+ {
+ memcpy(data, expect->retval->data, expect->retval->ndata);
+ }
+}
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xft/Xft.h>
+
+int XSync(Display* display, Bool discard)
+{
+ MockCall_T* call = MockCall(__func__, 2);
+ call->values[0] = MockValue("display", &display, sizeof(display));
+ call->values[1] = MockValue("discard", &discard, sizeof(discard));
+ int retval;
+ VerifyCall(call, &retval);
+ return retval;
+}
+
+void Expect_XSync(Display* display, Bool discard, int _rv)
+{
+ MockCall_T* call = MockCall(__func__, 2);
+ call->retval = MockValue(0, &_rv, sizeof(_rv));
+ call->values[0] = MockValue("display", &display, sizeof(display));
+ call->values[1] = MockValue("discard", &discard, sizeof(discard));
+ RegisterCall(call);
+}
+
+int XMapWindow(Display* display, Window w)
+{
+ MockCall_T* call = MockCall(__func__, 2);
+ call->values[0] = MockValue("display", &display, sizeof(display));
+ call->values[1] = MockValue("w", &w, sizeof(w));
+ int retval;
+ VerifyCall(call, &retval);
+ return retval;
+}
+
+void Expect_XMapWindow(Display* display, Window w, int _rv)
+{
+ MockCall_T* call = MockCall(__func__, 2);
+ call->retval = MockValue(0, &_rv, sizeof(_rv));
+ call->values[0] = MockValue("display", &display, sizeof(display));
+ call->values[1] = MockValue("w", &w, sizeof(w));
+ RegisterCall(call);
+}
+
--- /dev/null
+#include <stdc.h>
+#include <liba.h>
+#include <libui.h>
+#include "libui_impl.h"
+#include "mocks.h"
+#include "atf.h"
+
+char* ARGV0;
+
+UNITTEST(window_show_should_show_the_window)
+{
+ MockInit();
+ Expect_XMapWindow(NULL, 0, 0);
+ Expect_XSync(NULL, False, 0);
+ win_show(&(UIWin){0});
+}
--- /dev/null
+#!/usr/bin/env bash
+
+runner="runner.c"
+declare -a sources
+header=$(cat<<-EOF
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#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 <setjmp.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <time.h>
+
+#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(void)
+{
+ printf("%lu/%lu tests passed\n" , Total - Failed, Total);
+ 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++;
+ (void)print_results();
+ _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));
+ printf("Seed: %u\n", seed);
+ 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();
+}
+EOF
+
+cc="${CC:-cc}"
+"$cc" -DATF_TEST -g -o runner -O0 "$@" "$runner" && ./runner
+#rm -f runner "$runner"
-#!/bin/env ruby
+#!/usr/bin/env ruby
HEADER = <<-eos
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
typedef struct {
- char* name;
+ const char* name;
int ndata;
char data[];
} MockValue_T;
typedef struct {
- char* name;
+ const char* name;
MockValue_T* retval;
int nvalues;
MockValue_T* values[];
#define MAX_CALLS 256
#endif
+#ifndef MAX_MEMORY
+#define MAX_MEMORY ((1024*1024) / sizeof(int))
+#endif
+
+int* CurrentMem = 0;
int CurrentExpected = 0;
int CurrentCall = 0;
+int Memory[MAX_MEMORY];
MockCall_T* ExpectedCalls[MAX_CALLS];
MockCall_T* ActualCalls[MAX_CALLS];
+void FailMessage(char* fmt, ...)
+{
+ exit(1);
+}
+
+void* Allocate(int sz)
+{
+ int ncells = (sz / sizeof(int)) + ((sz % sizeof(int)) ? 1 : 0);
+ int* next = CurrentMem + ncells;
+ if (next > (Memory + (sizeof(Memory)/sizeof(Memory[0]))))
+ {
+ FailMessage("memory buffer exhausted");
+ }
+ memset(CurrentMem, 0, sizeof(int)*ncells);
+ int* curr = CurrentMem;
+ CurrentMem = next;
+ return curr;
+}
+
void MockInit(void)
{
+ CurrentMem = Memory;
CurrentExpected = 0;
CurrentCall = 0;
}
-/* REGISTER MOCK CALL */
-/* allocate call structure with N values */
-/* foreach arg */
- /* allocate named value structure */
- /* initialize named value structure */
- /* assign named value structure to slot in mock call */
-/* if call returns non-void */
- /* allocate value structure */
- /* initialize value structure */
- /* assign value structure to slot in mock call */
+MockValue_T* MockValue(const char* name, void* data, int ndata)
+{
+ MockValue_T* val = Allocate(sizeof(MockValue_T) + ndata);
+ val->name = name;
+ val->ndata = ndata;
+ memcpy(val->data, data, ndata);
+ return val;
+}
+
+MockCall_T* MockCall(const char* name, int nargs)
+{
+ MockCall_T* call = Allocate(sizeof(MockCall_T) + (nargs * sizeof(MockValue_T*)));
+ call->name = name;
+ call->nvalues = nargs;
+ return call;
+}
+
+void RegisterCall(MockCall_T* call)
+{
+ if (CurrentExpected >= MAX_CALLS)
+ {
+ FailMessage("exhausted expected call buffer");
+ }
+ ExpectedCalls[CurrentExpected++] = call;
+}
+
+void VerifyCall(MockCall_T* call, void* data)
+{
+ if (CurrentCall >= CurrentExpected)
+ {
+ FailMessage("");
+ }
+ MockCall_T* expect = ExpectedCalls[CurrentCall];
+ if (0 != strcmp(expect->name, call->name))
+ {
+ FailMessage("expected a call to '%s', received a call to '%s' instead", expect->name, call->name);
+ }
+ assert(expect->nvalues == call->nvalues);
+ for (int i = 0; i < call->nvalues; i++)
+ {
+ MockValue_T* earg = expect->values[i];
+ MockValue_T* rarg = call->values[i];
+ assert(earg->ndata == rarg->ndata);
+ if (0 != memcmp(earg->data, rarg->data, earg->ndata))
+ {
+ FailMessage("received unexpected value for argument '%s'", earg->name);
+ }
+ }
+ if (expect->retval)
+ {
+ memcpy(data, expect->retval->data, expect->retval->ndata);
+ }
+}
eos
end
def genmock(name, args, rettype)
- args = args.map {|a| "#{a[:type]} #{a[:name]}" }
- puts "#{rettype} #{name}(#{args.join(", ")})\n{\n"
+ argstrings = args.map {|a| "#{a[:type]} #{a[:name]}" }
+ puts "#{rettype} #{name}(#{argstrings.join(", ")})\n{\n"
+ puts " MockCall_T* call = MockCall(__func__, #{args.length});"
+ args.each_with_index do |a,i|
+ puts " call->values[#{i}] = MockValue(\"#{a[:name]}\", &#{a[:name]}, sizeof(#{a[:name]}));"
+ end
+ if rettype != "void"
+ puts " #{rettype} retval;"
+ puts " VerifyCall(call, &retval);"
+ puts " return retval;"
+ else
+ puts " VerifyCall(call, NULL);"
+ end
+ puts "}"
+ puts
+end
+
+def genexpect(name, args, rettype)
+ argstrings = args.map {|a| "#{a[:type]} #{a[:name]}" }
+ retarg = (rettype == "void" ? "" : ", #{rettype} _rv");
+ puts "void Expect_#{name}(#{argstrings.join(", ")}#{retarg})\n{\n"
+ puts " MockCall_T* call = MockCall(__func__, #{args.length});"
+ if rettype != "void"
+ puts " call->retval = MockValue(0, &_rv, sizeof(_rv));"
+ end
+ args.each_with_index do |a,i|
+ puts " call->values[#{i}] = MockValue(\"#{a[:name]}\", &#{a[:name]}, sizeof(#{a[:name]}));"
+ end
+ puts " RegisterCall(call);"
puts "}"
puts
end
+puts HEADER
File.read(ARGV[0]).each_line do |ln|
ln = ln.gsub(/(const|volatile)/,'')
if ln =~ /(.*) ([_a-zA-Z0-9]+)\((.*)\)/
args = (args == "void" ? [] : args.split(","))
args = parse_args(args)
genmock(name, args, rettype)
+ genexpect(name, args, rettype)
else
puts ln
end