]> git.mdlowis.com Git - proto/aos.git/commitdiff
setup unit test framework
authorMike Lowis <mike.lowis@gentex.com>
Tue, 25 Apr 2023 16:58:00 +0000 (12:58 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Tue, 25 Apr 2023 16:58:00 +0000 (12:58 -0400)
Makefile
inc/atf.h
mkrules
test/libui/window_show.c
tools/atf [deleted file]

index 582845d3600752bd17aa3a6b6cd91b9ca53a7a47..371b9e62c4d36d34a5802e75cbd8134d448471d5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,14 @@
-.PHONY: all libs bins rules clean
+.PHONY: all libs bins tests rules clean
 
-all: libs bins
+all: bins
 
 clean:
        rm -rf $(OUTDIR) rules.mk
 
-bins: libs
+bins: libs tests
+
+tests:
+       for f in build/test/*; do $$f; done
 
 rules:
        ./mkrules $(OUTDIR)
@@ -16,3 +19,4 @@ rules.mk:
 include config.mk
 include rules.mk
 include bin/rules.mk
+include test/rules.mk
index 5ce11c33ab5f270bbea90c2b7a3b2dd29ce46501..af5d5522738912b8a20e75646ec82659aa3263ec 100644 (file)
--- a/inc/atf.h
+++ b/inc/atf.h
-#include <stdint.h>
+/**
+    Aardvark Test Framework - A minimalistic unit testing framework for C.
+
+    Copyright 2014 Michael D. Lowis
+
+    Permission to use, copy, modify, and/or distribute this software
+    for any purpose with or without fee is hereby granted, provided
+    that the above copyright notice and this permission notice appear
+    in all copies.
+
+    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+    WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+    AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+    DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+    TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+    PERFORMANCE OF THIS SOFTWARE.
+*/
+#ifndef ATF_H
+#define ATF_H
+
+#include <stddef.h>
 #include <stdbool.h>
+#include <setjmp.h>
+#include <assert.h>
+
+extern char* Curr_Test;
+void atf_init(int argc, char** argv);
+void atf_test_start(char* file, unsigned int line, char* name);
+bool atf_test_assert(bool success, char* expr_str, char* file, int line);
+void atf_test_fail(char* expr, char* file, int line);
+int atf_print_results(void);
+
+#define IGNORE(msg) \
+    printf("%s:%d:%s:IGNORE:\n\t%s\n", __FILE__, __LINE__, Curr_Test, msg); break
+
+#define CHECK(expr) \
+    if(atf_test_assert((expr), #expr, __FILE__, __LINE__)) break
+
+#define CHECK_EXITCODE(code) \
+    CHECK(ExitCode == code)
+
+#define TEST_SUITE(name) \
+    void name(void)
+
+#define TEST(desc) \
+    for(atf_test_start(__FILE__,__LINE__,#desc); Curr_Test != NULL; Curr_Test = NULL)
+
+#define RUN_TEST_SUITE(name) \
+    name();
+
+#define RUN_EXTERN_TEST_SUITE(name) \
+    do { extern TEST_SUITE(name); RUN_TEST_SUITE(name); } while(0)
+
+#define EXPECT_EXIT \
+    if ((ExitExpected = true, 0 == setjmp(ExitPad)))
+
+/* Function Definitions
+ *****************************************************************************/
+#ifdef INCLUDE_DEFS
+#include <stdio.h>
 #include <stdlib.h>
+#ifndef NO_SIGNALS
+#include <signal.h>
+#endif
+
+char* Curr_Test = NULL;
+char* Curr_File = NULL;
+char* Suite_Path = NULL;
+unsigned int Curr_Line = 0;
+static unsigned int Total = 0;
+static unsigned int Failed = 0;
+bool ExitExpected;
+int ExitCode;
+jmp_buf ExitPad;
+
+#ifndef NO_SIGNALS
+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:0:%s:CRASH (signal: %d - %s)\n", Curr_File, Curr_Line, Curr_Test, sig, sig_name);
+    Failed++;
+    (void)atf_print_results();
+    _Exit(1);
+}
+#endif
+
+void atf_init(int argc, char** argv) {
+    /* I reserve the right to use these later */
+    Suite_Path = argv[0];
+    (void)argc;
+
+#ifndef NO_SIGNALS
+    /* Init signal handler */
+    signal(SIGABRT, handle_signal);
+    signal(SIGBUS,  handle_signal);
+    signal(SIGFPE,  handle_signal);
+    signal(SIGILL,  handle_signal);
+    signal(SIGSEGV, handle_signal);
+    signal(SIGSYS,  handle_signal);
+#endif
+}
+
+void atf_test_start(char* file, unsigned int line, char* name) {
+    Curr_File = file;
+    Curr_Line = line;
+    Curr_Test = name;
+    Total++;
+}
+
+bool atf_test_assert(bool success, char* expr, char* file, int line) {
+    bool failed = !success;
+    if (failed) atf_test_fail(expr,file,line);
+    return failed;
+}
+
+void atf_test_fail(char* expr, char* file, int line) {
+    Failed++;
+    printf("%s:%d:0:%s:FAIL:( %s )\n", file, line, Curr_Test, expr); \
+}
+
+int atf_print_results(void) {
+    int count = printf("\n%s\n", Suite_Path);
+    for (int i = 1; i < count; i++)
+    {
+        putchar('-');
+    }
+    static const char* results_string =
+    "\nTotal:  %d"
+    "\nPassed: %d"
+    "\nFailed: %d"
+    "\n\n";
+    printf(results_string, Total, Total - Failed, Failed);
+    return Failed;
+}
+
+void exit(int code) {
+    if (ExitExpected) {
+        ExitCode = code;
+        ExitExpected = false;
+        longjmp(ExitPad, 1);
+    } else {
+        assert(!"Unexpected exit. Something went wrong");
+    }
+}
 
-#ifdef ATF_TEST
-    #define main MAIN
-    #define UNITTEST(name,...) void name(void)
-#else
-    #define UNITTEST(name,...) static inline void name(void)
+#undef INCLUDE_DEFS
 #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);
+#endif /* ATF_H */
diff --git a/mkrules b/mkrules
index 2fe8e69904c72102acf3ebd5c8a3d2d93e98f6e6..c053b033f5c2482b6023d37bb933d6b60ab5493f 100755 (executable)
--- a/mkrules
+++ b/mkrules
@@ -24,6 +24,7 @@ libs=""
     mkdir -p "$OUTDIR/bin/"
     mkdir -p "$OUTDIR/lib/"
     mkdir -p "$OUTDIR/obj/"
+    mkdir -p "$OUTDIR/test/"
 
     # Generate rules for all libraries
     for lib in lib/*; do
@@ -55,4 +56,12 @@ libs=""
         printf "\t\$(BINARY)\n"
         puts "bins: \$(OUTDIR)/${bin%/}"
     done
+
+    # Generate rules for test binaries
+    for test in test/*/ ; do
+       find "${test%/}" -name '*.c' | ObjectRules "\$(OUTDIR)/${test%/}"
+        puts "\$(OUTDIR)/${test%/}: | \$(libs)"
+        printf "\t\$(BINARY)\n"
+        puts "tests: \$(OUTDIR)/${test%/}"
+    done
 } >> rules.mk
index 7df0e54b84c4334e915eff15dfdcccc05e38aa22..701f193b0def2f7b7ff5288e351ef025d9c522b7 100644 (file)
@@ -1,16 +1,25 @@
-#include <stdc.h>
-#include <liba.h>
-#include <libui.h>
-#include "libui_impl.h"
-#include "mocks.h"
+//#include <stdc.h>
+//#include <liba.h>
+//#include <libui.h>
+//#include "libui_impl.h"
+//#include "mocks.h"
+
+#define INCLUDE_DEFS
 #include "atf.h"
 
-char* ARGV0;
+//
+//char* ARGV0;
+//
+//UNITTEST(window_show_should_show_the_window)
+//{
+//    MockInit();
+//    Expect_XMapWindow(NULL, 0, 0);
+//    Expect_XSync(NULL, False, 0);
+//    win_show(&(UIWin){0});
+//}
 
-UNITTEST(window_show_should_show_the_window)
+int main(int argc, char** argv)
 {
-    MockInit();
-    Expect_XMapWindow(NULL, 0, 0);
-    Expect_XSync(NULL, False, 0);
-    win_show(&(UIWin){0});
+    atf_init(argc, argv);
+    return atf_print_results();
 }
diff --git a/tools/atf b/tools/atf
deleted file mode 100755 (executable)
index aada39b..0000000
--- a/tools/atf
+++ /dev/null
@@ -1,353 +0,0 @@
-#!/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"