]> git.mdlowis.com Git - proto/libregexp.git/commitdiff
added some automated unit tests to help with refactoring without brekaing
authorMichael D. Lowis <mike.lowis@gentex.com>
Wed, 19 Dec 2018 15:40:15 +0000 (10:40 -0500)
committerMichael D. Lowis <mike.lowis@gentex.com>
Wed, 19 Dec 2018 15:40:15 +0000 (10:40 -0500)
Makefile
atf.h [new file with mode: 0644]
tests.c [new file with mode: 0644]

index 93ee4a3420b491789d1e27a1865bb6c6d913143b..f103f6cab0bc9183c0f9ed9bcf47371e39399990 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ HFILES=\
        regcomp.h\
        utf.h\
 
-all: $(LIB)
+all: tests
 
 install: $(LIB)
        mkdir -p $(PREFIX)/share/man/man3 $(PREFIX)/share/man/man7
@@ -37,6 +37,11 @@ test: test.$O $(LIB)
 test2: test2.$O $(LIB)
        $(CC) -o test2 test2.$O $(LIB)
 
+tests: tests.$O $(LIB)
+       $(CC) -o tests tests.$O $(LIB)
+       ./tests
+
+
 $(LIB): $(OFILES)
        $(AR) $(ARFLAGS) $(LIB) $(OFILES)
        $(RANLIB) $(LIB)
@@ -47,4 +52,4 @@ $(LIB): $(OFILES)
 $(OFILES): $(HFILES)
 
 clean:
-       rm -f $(OFILES) $(LIB)
+       rm -f $(LIB) test test2 tests *.o
diff --git a/atf.h b/atf.h
new file mode 100644 (file)
index 0000000..465ac44
--- /dev/null
+++ b/atf.h
@@ -0,0 +1,128 @@
+/**
+  @brief A minimalistic unit testing framework for C.
+  @author Michael D. Lowis
+  @license BSD 2-clause License
+*/
+#ifndef ATF_H
+#define ATF_H
+
+#include <stddef.h>
+#include <stdbool.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 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 PRINT_TEST_RESULTS \
+    atf_print_results
+
+/* 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;
+unsigned int Curr_Line = 0;
+static unsigned int Total = 0;
+static unsigned int Failed = 0;
+
+#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 */
+    (void)argc;
+    (void)argv;
+
+#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) {
+    static const char* results_string =
+    "\nUnit Test Summary"
+    "\n-----------------"
+    "\nTotal:  %d"
+    "\nPassed: %d"
+    "\nFailed: %d"
+    "\n\n";
+    printf(results_string, Total, Total - Failed, Failed);
+    return Failed;
+}
+
+#undef INCLUDE_DEFS
+#endif
+
+#endif /* ATF_H */
diff --git a/tests.c b/tests.c
new file mode 100644 (file)
index 0000000..7694714
--- /dev/null
+++ b/tests.c
@@ -0,0 +1,105 @@
+#define INCLUDE_DEFS
+#include "atf.h"
+#include "regexp9.h"
+
+int main(int argc, char** argv) {
+    atf_init(argc,argv);
+    RUN_EXTERN_TEST_SUITE(MatchTests);
+    return atf_print_results();
+}
+
+int match(char* regex, char* str) {
+    Reprog* regprog = regcomp(regex);
+    int result = regexec(regprog, str, 0, 0);
+    free(regprog);
+    return (result == 1);
+}
+
+TEST_SUITE(MatchTests) {
+    TEST(/a/ should match 'a')
+        CHECK(match("a", "a"));
+
+    TEST(/a/ should not match 'b')
+        CHECK(!match("a", "b"));
+
+    TEST(/a/ should not match 'b')
+        CHECK(!match("a", "b"));
+
+    TEST(/^a/ should match 'ab')
+        CHECK(match("^a", "ab"));
+
+    TEST(/^a/ should not match 'ba')
+        CHECK(!match("^a", "ba"));
+
+    TEST(/a$/ should match 'ba')
+        CHECK(match("a$", "ba"));
+
+    TEST(/a$/ should not match 'ab')
+        CHECK(!match("a$", "ab"));
+
+    TEST(/./ should not match '')
+        CHECK(!match(".", ""));
+
+    TEST(/./ should match 'a')
+        CHECK(match(".", "a"));
+
+    TEST(/./ should match 'b')
+        CHECK(match(".", "b"));
+
+    TEST(/a*/ should match '')
+        CHECK(match("a*", ""));
+
+    TEST(/a*/ should match 'a')
+        CHECK(match("a*", "a"));
+
+    TEST(/a*/ should match 'aa')
+        CHECK(match("a*", "aa"));
+
+    TEST(/a+/ should not match '')
+        CHECK(!match("a+", ""));
+
+    TEST(/a+/ should match 'a')
+        CHECK(match("a+", "a"));
+
+    TEST(/a+/ should match 'aa')
+        CHECK(match("a+", "aa"));
+
+    TEST(/a+/ should match 'aaa')
+        CHECK(match("a+", "aaa"));
+
+    TEST(/a?/ should match '')
+        CHECK(match("a?", ""));
+
+    TEST(/a?/ should match 'a')
+        CHECK(match("a?", "a"));
+
+    TEST(/[b-c]/ should not match 'a')
+        CHECK(!match("[b-c]", "a"));
+
+    TEST(/[b-c]/ should match 'b')
+        CHECK(match("[b-c]", "b"));
+
+    TEST(/[b-c]/ should match 'c')
+        CHECK(match("[b-c]", "c"));
+
+    TEST(/[b-c]/ should not match 'd')
+        CHECK(!match("[b-c]", "d"));
+
+    TEST(/^a(bc)d$/ should match 'abcd')
+        CHECK(match("^a(bc)d$", "abcd"));
+
+    TEST(/^a(bc)d$/ should not match 'abd')
+        CHECK(!match("^a(bc)d$", "abd"));
+
+    TEST(/^a(bc)d$/ should not match 'acd')
+        CHECK(!match("^a(bc)d$", "acd"));
+
+    TEST(/^a|b/ should not match 'c')
+        CHECK(!match("a|b", "c"));
+
+    TEST(/^a|b/ should match 'a')
+        CHECK(match("a|b", "a"));
+
+    TEST(/^a|b/ should match 'b')
+        CHECK(match("a|b", "b"));
+}
\ No newline at end of file