]> git.mdlowis.com Git - projs/tide.git/commitdiff
fleshed out qcheck more and added property tests for buf.c
authorMichael D. Lowis <mike@mdlowis.com>
Fri, 1 Mar 2019 03:46:31 +0000 (22:46 -0500)
committerMichael D. Lowis <mike@mdlowis.com>
Fri, 1 Mar 2019 03:46:31 +0000 (22:46 -0500)
config.mk
inc/atf.h
inc/qcheck.h [new file with mode: 0644]
tests/lib/buf.c
tests/libedit.c
tests/test_tide.c

index d7a9e035bb42945aef35b4d6e0604ffdb194a77b..0611cddcf833e6dd95753c05827886eebf8a1f8d 100644 (file)
--- a/config.mk
+++ b/config.mk
@@ -41,5 +41,5 @@ ARFLAGS = rcs
 #LDFLAGS += -pg
 
 # GCC/Clang Coverage
-#CFLAGS  += -g -O0 --coverage
-#LDFLAGS += -g -O0 --coverage
+CFLAGS  += -g -O0 --coverage
+LDFLAGS += -g -O0 --coverage
index 465ac44a99b38af773f3ece1a47b1521ea5088f5..ca7b30ac169d7ad05b3a9f3a20b72921c4a17396 100644 (file)
--- a/inc/atf.h
+++ b/inc/atf.h
@@ -1,13 +1,29 @@
 /**
-  @brief A minimalistic unit testing framework for C.
-  @author Michael D. Lowis
-  @license BSD 2-clause License
+    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);
@@ -22,6 +38,9 @@ int atf_print_results(void);
 #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)
 
@@ -34,8 +53,8 @@ int atf_print_results(void);
 #define RUN_EXTERN_TEST_SUITE(name) \
     do { extern TEST_SUITE(name); RUN_TEST_SUITE(name); } while(0)
 
-#define PRINT_TEST_RESULTS \
-    atf_print_results
+#define EXPECT_EXIT \
+    if ((ExitExpected = true, 0 == setjmp(ExitPad)))
 
 /* Function Definitions
  *****************************************************************************/
@@ -51,6 +70,9 @@ char* Curr_File = 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) {
@@ -122,7 +144,15 @@ int atf_print_results(void) {
     return Failed;
 }
 
-#undef INCLUDE_DEFS
+void exit(int code) {
+    if (ExitExpected) {
+        ExitCode = code;
+        ExitExpected = false;
+        longjmp(ExitPad, 1);
+    } else {
+        assert(!"Unexpected exit. Something went wrong");
+    }
+}
 #endif
 
 #endif /* ATF_H */
diff --git a/inc/qcheck.h b/inc/qcheck.h
new file mode 100644 (file)
index 0000000..7aec0ce
--- /dev/null
@@ -0,0 +1,234 @@
+/**
+    QCheck - A minimalistic property based testing framework.
+
+    Copyright 2019 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 QCHECK_H
+#define QCHECK_H
+
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+
+typedef struct QCValue {
+    void (*showfn)(struct QCValue* val);
+    void (*freefn)(struct QCValue* val);
+    long ndata;
+    long data[];
+} QCValue;
+
+typedef struct {
+    int status;
+    int nvals;
+    QCValue** vals;
+} QCResult;
+
+typedef int (*QCProp)(int nvals, QCValue** vals);
+
+typedef QCValue* (*QCGenFn)(void);
+
+#define VAL(v, type) ((type)((v)->data[0]))
+
+void qcinit(int seed);
+void qcntrials(int ntrials);
+QCValue* qcalloc(size_t sz, void* data);
+long qcrandr(long from, long to);
+long qcrand(void);
+void qcfree(int nvals, QCValue** vals);
+void qcshow(int nvals, QCValue** vals);
+QCResult vqcheck(QCProp prop, int nvals, va_list vals);
+int qcheck(char* desc, QCProp prop, int nvals, ...);
+
+void ShowLong(QCValue* val);
+void ShowBool(QCValue* val);
+void ShowChar(QCValue* val);
+
+QCValue* MkLong(long val, void (*showfn)(QCValue* val));
+QCValue* GenLongR(long from, long to);
+QCValue* GenLong(void);
+QCValue* GenU8(void);
+QCValue* GenU16(void);
+QCValue* GenU32(void);
+QCValue* GenBool(void);
+QCValue* GenByte(void);
+QCValue* GenAsciiChar(void);
+QCValue* GenAsciiString(void);
+
+/* Function Definitions
+ *****************************************************************************/
+#ifdef INCLUDE_DEFS
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static int Seed = 0, NTrials = 1000;
+
+void qcinit(int seed) {
+    Seed = (seed ? seed : time(NULL));
+    srand(Seed);
+}
+
+void qcntrials(int ntrials) {
+    NTrials = ntrials;
+}
+
+QCValue* qcalloc(size_t sz, void* data) {
+    QCValue* value = calloc(1, sizeof(QCValue) + sz);
+    if (data) memcpy(value->data, data, sz);
+    value->ndata = 1;
+    return value;
+}
+
+long qcrandr(long from, long to) {
+    return ((rand() % (to - from + 1)) + from);
+}
+
+long qcrand(void) {
+    return qcrandr(0, RAND_MAX);
+}
+
+void qcfree(int nvals, QCValue** vals) {
+    for (int i = 0; i < nvals; i++) {
+        if (vals[i]->freefn) vals[i]->freefn(vals[i]);
+        free(vals[i]);
+    }
+    free(vals);
+}
+
+void qcshow(int nvals, QCValue** vals) {
+    for (int i = 0; i < nvals; i++) {
+        printf("Argument %d: ", i);
+        vals[i]->showfn(vals[i]);
+    }
+}
+
+QCResult vqcheck(QCProp prop, int nvals, va_list vals) {
+    /* generate the input values */
+    QCValue** values = NULL;
+    if (nvals) {
+        values = malloc(sizeof(QCValue*) * nvals);
+        for (int i = 0; i < nvals; i++)
+            values[i] = (va_arg(vals, QCGenFn))();
+    }
+    /* run the test and get the result */
+    QCResult result = { .status = 0 };
+    result.status = prop(nvals, values);
+    result.nvals = nvals;
+    result.vals = values;
+    return result;
+}
+
+int qcheck(char* desc, QCProp prop, int nvals, ...) {
+    int passed = 0;
+    QCResult result;
+    va_list vals;
+    for (int i = 0; i < NTrials; i++) {
+        va_start(vals, nvals);
+        result = vqcheck(prop, nvals, vals);
+        va_end(vals);
+        if (!result.status) break;
+        qcfree(result.nvals, result.vals);
+        passed++;
+    }
+    /* show 'em the results */
+    if (passed == NTrials) {
+        printf("%d tests passed for property: %s\n", passed, desc);
+    } else if (!result.status) {
+        printf("Property: %s\nFalsifiable after %d tests (seed: %d)\n", desc, passed+1, Seed);
+        qcshow(result.nvals, result.vals);
+        qcfree(result.nvals, result.vals);
+        return 0;
+    }
+    return 1;
+}
+
+void ShowLong(QCValue* val) {
+    printf("%ld\n", (val->data[0]));
+}
+
+void ShowBool(QCValue* val) {
+    printf("%s\n", (val->data[0] ? "true" : "false"));
+}
+
+void ShowChar(QCValue* val) {
+    printf("'%c'\n", (char)(val->data[0]));
+}
+
+void ShowByte(QCValue* val) {
+    printf("0x%02x\n", (char)(val->data[0]));
+}
+
+void ShowString(QCValue* val) {
+    printf("'%s'\n", (char*)(val->data));
+}
+
+QCValue* MkLong(long val, void (*showfn)(QCValue* val)) {
+    QCValue* value = qcalloc(sizeof(long), &val);
+    value->showfn = showfn;
+    return value;
+}
+
+QCValue* MkArray(size_t nelems, size_t elemsz, void (*showfn)(QCValue* val)) {
+    QCValue* value = qcalloc(nelems * elemsz, NULL);
+    value->showfn = showfn;
+    return value;
+}
+
+QCValue* GenLongR(long from, long to) {
+    return MkLong(qcrandr(from, to), ShowLong);
+}
+
+QCValue* GenLong(void) {
+    return MkLong(qcrand(), ShowLong);
+}
+
+QCValue* GenU8(void) {
+    return GenLongR(0, UINT8_MAX);
+}
+
+QCValue* GenU16(void) {
+    return GenLongR(0, UINT16_MAX);
+}
+
+QCValue* GenU32(void) {
+    return GenLongR(0, UINT32_MAX);
+}
+
+QCValue* GenBool(void) {
+    return MkLong(qcrandr(0, 1), ShowBool);
+}
+
+QCValue* GenByte(void) {
+    return MkLong(qcrandr(0, 255), ShowByte);
+}
+
+QCValue* GenAsciiChar(void) {
+    return MkLong(qcrandr(32, 127), ShowChar);
+}
+
+QCValue* GenAsciiString(void) {
+    size_t nelem = qcrandr(1, 1024);
+    QCValue* value = MkArray(nelem+1, 1, ShowString);
+    char* string = (char*)value->data;
+    for (size_t i = 0; i < nelem; i++)
+        *(string++) = qcrandr(32,127);
+    return value;
+}
+
+#endif
+
+#endif /* QCHECK_H */
index 13481e5376a4bc64e8d290df79c2b6f94cccac81..6be91e5759b0098a3b2c02c62601505e38249292 100644 (file)
@@ -1,4 +1,5 @@
 #include <atf.h>
+#include <qcheck.h>
 #include <stdc.h>
 #include <utf.h>
 #include <edit.h>
@@ -11,17 +12,38 @@ static void set_buffer_text(char* str) {
     buf_puts(&TestBuf, str);
 }
 
-/*
-static bool buf_text_eq(char* str) {
-    buf_selall(&TestBuf);
-    char* bstr = buf_gets(&TestBuf);
-    int ret = strcmp(str, bstr);
-    free(bstr);
-    return (ret == 0);
+#define QCHECK(desc, prop, ...) \
+    CHECK(qcheck(desc, prop, __VA_ARGS__))
+
+int getc_returns_putc(int nvals, QCValue** vals) {
+    (void)nvals;
+    Buf buf = {0};
+    buf_init(&buf);
+    buf_putc(&buf, vals[0]->data[0]);
+    buf.selection.end = buf.selection.beg = 0;
+    return (vals[0]->data[0] == buf_getc(&buf));
+}
+
+int gets_returns_puts(int nvals, QCValue** vals) {
+    (void)nvals;
+    Buf buf = {0};
+    buf_init(&buf);
+    char* input = (char*)(vals[0]->data);
+    buf_puts(&buf, input);
+    buf.selection.beg = 0;
+    buf.selection.end = buf_end(&buf);
+    char* output = buf_gets(&buf);
+    return (!strcmp(input,output));
 }
-*/
 
 TEST_SUITE(BufferTests) {
+    TEST(buf should adhere to specific properties) {
+        QCHECK("getc should return the same printable ascii value inserted with putc",
+            getc_returns_putc, 1, GenAsciiChar);
+        QCHECK("gets should return the same printable ascii string inserted with puts",
+            gets_returns_puts, 1, GenAsciiString);
+    }
+
     /* Initializing
      *************************************************************************/
     TEST(buf_init should initialize an empty buffer) {
index 9f710261b1ebde9c954de38e32114851dfb26bce..ffb783dab4d7fd482f54a58e1b4d97c8f511f13b 100644 (file)
@@ -1,13 +1,15 @@
 #include <stdc.h>
 #include <utf.h>
 #include <edit.h>
+
 #define INCLUDE_DEFS
 #include <atf.h>
-#define INCLUDE_DEFS
+#include <qcheck.h>
 #include "config.h"
 
 int main(int argc, char** argv) {
-    atf_init(argc,argv);
+    qcinit(argc >= 2 ? strtol(argv[1], 0, 0) : 0);
+    atf_init(argc, argv);
     RUN_EXTERN_TEST_SUITE(BufferTests);
     RUN_EXTERN_TEST_SUITE(Utf8Tests);
     return atf_print_results();
index e3663eda5c1f614938603d4509e2988144162db5..513d30156cdf8d5604c6e64b08e94b6609daaf28 100644 (file)
@@ -1,154 +1,8 @@
-#include <time.h>
-
 #define TEST
 #include "src/tide.c"
 
-typedef struct QCValue QCValue;
-struct QCValue {
-    void (*showfn)(QCValue* val);
-    void (*freefn)(QCValue* val);
-    long ndata;
-    long data[];
-};
-
-typedef struct {
-    int status;
-    int nvals;
-    QCValue** vals;
-} QCResult;
-
-typedef int (*QCProp)(int nvals, QCValue** vals);
-
-typedef QCValue* (*QCGenFn)(void);
-
-int Seed = 0, NTrials = 10000;
-
-void qcinit(int seed) {
-    Seed = (seed ? seed : time(NULL));
-    srandom(Seed);
-}
-
-void qcntrials(int ntrials) {
-    NTrials = ntrials;
-}
-
-QCValue* qcalloc(size_t sz, void* data) {
-    QCValue* value = calloc(1, sizeof(QCValue) + sz);
-    memcpy(value->data, data, sz);
-    return value;
-}
-
-long qcrandr(long from, long to) {
-    return ((random() % (to - from + 1)) + from);
-}
-
-long qcrand(void) {
-    return qcrandr(0, RAND_MAX);
-}
-
-void qcfree(int nvals, QCValue** vals) {
-    for (int i = 0; i < nvals; i++) {
-        if (vals[i]->freefn) vals[i]->freefn(vals[i]);
-        free(vals[i]);
-    }
-    free(vals);
-}
-
-void qcshow(int nvals, QCValue** vals) {
-    for (int i = 0; i < nvals; i++) {
-        printf("Argument %d: ", i);
-        vals[i]->showfn(vals[i]);
-    }
-}
-
-QCResult vqcheck(QCProp prop, int nvals, va_list vals) {
-    /* generate the input values */
-    QCValue** values = NULL;
-    if (nvals) {
-        values = malloc(sizeof(QCValue*) * nvals);
-        for (int i = 0; i < nvals; i++)
-            values[i] = (va_arg(vals, QCGenFn))();
-    }
-    /* run the test and get the result */
-    QCResult result = { .status = 0 };
-    result.status = prop(nvals, values);
-    result.nvals = nvals;
-    result.vals = values;
-    return result;
-}
-
-int qcheck(char* desc, QCProp prop, int nvals, ...) {
-    int passed = 0;
-    QCResult result;
-    va_list vals;
-    for (int i = 0; i < NTrials; i++) {
-        va_start(vals, nvals);
-        result = vqcheck(prop, nvals, vals);
-        va_end(vals);
-        if (!result.status) break;
-        qcfree(result.nvals, result.vals);
-        passed++;
-    }
-    /* show 'em the results */
-    if (passed == NTrials) {
-        printf("%d tests passed for property: %s\n", passed, desc);
-    } else if (!result.status) {
-        printf("Property: %s\nFalsifiable after %d tests (seed: %d)\n", desc, passed+1, Seed);
-        qcshow(result.nvals, result.vals);
-        /* should investigate shrinking input here as well */
-        qcfree(result.nvals, result.vals);
-        return 0;
-    }
-    return 1;
-}
-
-/************************************************/
-
-void ShowLong(QCValue* val) {
-    printf("%ld\n", (val->data[0]));
-}
-
-void ShowBool(QCValue* val) {
-    printf("%s\n", (val->data[0] ? "true" : "false"));
-}
-
-void ShowChar(QCValue* val) {
-    printf("'%c'\n", (char)(val->data[0]));
-}
-
-QCValue* MkLong(long val, void (*showfn)(QCValue* val)) {
-    QCValue* value = qcalloc(sizeof(long), &val);
-    value->showfn = showfn;
-    return value;
-}
-
-QCValue* GenLongR(long from, long to) {
-    return MkLong(qcrandr(from, to), ShowLong);
-}
-
-QCValue* GenLong(void) {
-    return MkLong(qcrand(), ShowLong);
-}
-
-QCValue* GenU8(void) {
-    return GenLongR(0, UINT8_MAX);
-}
-
-QCValue* GenU16(void) {
-    return GenLongR(0, UINT16_MAX);
-}
-
-QCValue* GenU32(void) {
-    return GenLongR(0, UINT32_MAX);
-}
-
-QCValue* GenBool(void) {
-    return MkLong(qcrandr(0, 1), ShowBool);
-}
-
-QCValue* GenChar(void) {
-    return MkLong(qcrandr(0, 127), ShowChar);
-}
+#define INCLUDE_DEFS
+#include "qcheck.h"
 
 /************************************************/
 
@@ -176,12 +30,12 @@ int resizing_should_not_crash(int nvals, QCValue** vals) {
 //    int pid = fork();
 //    /* test the resize event */
 //    if (pid == 0) {
-        XEvent e = {0};
-        e.xconfigure.width = vals[0]->data[0];
-        e.xconfigure.height = vals[1]->data[0];
-        tide_init();
-        (X.eventfns[ConfigureNotify])(&X, &e);
-        xupdate(NULL);
+//        XEvent e = {0};
+//        e.xconfigure.width = vals[0]->data[0];
+//        e.xconfigure.height = vals[1]->data[0];
+//        tide_init();
+//        (X.eventfns[ConfigureNotify])(&X, &e);
+//        xupdate(NULL);
 //    }
 //    switch (fork()) {
 //        case 0:
@@ -195,12 +49,10 @@ int resizing_should_not_crash(int nvals, QCValue** vals) {
 }
 
 int main(int argc, char** argv) {
-    (void)argc, (void)argv, (void)usage;
+    (void)usage;
 
-    qcinit(0);
+    qcinit(argc >= 2 ? strtol(argv[1], 0, 0) : 0);
     qcheck("all numbers are divisible by 2", divisible_by_two, 1, GenLong);
-    qcheck("resizing should not crash the app",
-        resizing_should_not_crash, 2, GenU16, GenU16);
 
     return 0;
 }