]> git.mdlowis.com Git - proto/sclpl.git/commitdiff
setup test framework and added first test
authorMichael D. Lowis <mike@mdlowis.com>
Sun, 31 Mar 2019 03:35:12 +0000 (23:35 -0400)
committerMichael D. Lowis <mike@mdlowis.com>
Sun, 31 Mar 2019 03:35:12 +0000 (23:35 -0400)
.gitignore
Makefile
inc/atf.h [new file with mode: 0644]
src/lex.c
test/lex.c [new file with mode: 0644]
test/main.c [new file with mode: 0644]
test/parser.c [new file with mode: 0644]

index c85b4557b4f59cb9ff8f25bb203f0c5a60ddab22..43e96d0f6cbf4070cb2e4d94288e70987a15dfce 100644 (file)
@@ -13,3 +13,4 @@ cscope.out
 sclpl
 source/lexer.c
 project.vim
+tests
index 4e3257976cd2c2ead7cf685c25c908d51d55c7b6..2f25fb55fb81c2ed3d6822a0ac9004dd8f56b7c5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -15,6 +15,10 @@ LDFLAGS  += ${LIBS}
 ARFLAGS   = rcs
 MAKEFLAGS = -j
 
+# GCC - Enable Sanitizers
+CFLAGS += -g -fsanitize=address,undefined
+LDFLAGS += -g -fsanitize=address,undefined
+
 #------------------------------------------------------------------------------
 # Build Targets and Rules
 #------------------------------------------------------------------------------
@@ -30,9 +34,15 @@ OBJS = \
        src/syms.o    \
        src/codegen.o
 
-.PHONY: all specs tests
+TESTBIN = tests
+TESTOBJS = \
+       test/main.o    \
+       test/lex.o     \
+       test/parser.o  \
+
+.PHONY: all specs runtests
 
-all: ${BIN} tests
+all: ${BIN} runtests
 
 lib${BIN}.a: ${OBJS}
        ${AR} ${ARFLAGS} $@ $^
@@ -40,12 +50,16 @@ lib${BIN}.a: ${OBJS}
 ${BIN}: lib${BIN}.a
        ${LD} ${LDFLAGS} -o $@ $^
 
-specs: $(BIN)
+${TESTBIN}: ${TESTOBJS} lib${BIN}.a
+       ${LD} ${LDFLAGS} -o $@ $^
+
+specs: ${BIN}
        rspec --pattern 'spec/**{,/*/**}/*_spec.rb' --format documentation
 
-tests: $(BIN)
-       @echo "Parsing example file..."
-       ./sclpl -Aast example.src
+runtests: ${BIN} ${TESTBIN}
+#      @echo "Parsing example file..."
+#      ./sclpl -Aast example.src
+       ./${TESTBIN}
 
 .c.o:
        ${CC} ${CFLAGS} -c -o $@ $<
diff --git a/inc/atf.h b/inc/atf.h
new file mode 100644 (file)
index 0000000..40088ed
--- /dev/null
+++ b/inc/atf.h
@@ -0,0 +1,160 @@
+/**
+    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;
+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 */
+    (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;
+}
+
+void exit(int code) {
+    if (ExitExpected) {
+        ExitCode = code;
+        ExitExpected = false;
+        longjmp(ExitPad, 1);
+    } else {
+        assert(!"Unexpected exit. Something went wrong");
+    }
+}
+
+#undef INCLUDE_DEFS
+#endif
+
+#endif /* ATF_H */
\ No newline at end of file
index 1351a9843dcf6e5bd16c5844805d4e8555d941ab..eaf89fc6a11e87c4fd5330485cd02edec465f23a 100644 (file)
--- a/src/lex.c
+++ b/src/lex.c
@@ -187,7 +187,7 @@ static inline void readtok(Parser* ctx) {
 }
 
 void lexfile(Parser* ctx, char* path) {
-    LexFile* file = calloc(sizeof(file), 1u);
+    LexFile* file = calloc(sizeof(LexFile), 1u);
     file->path = strdup(path);
     file->fbeg = file->fpos = file_load(path);
     file->next = ctx->file;
diff --git a/test/lex.c b/test/lex.c
new file mode 100644 (file)
index 0000000..bdde76a
--- /dev/null
@@ -0,0 +1,4 @@
+#include <atf.h>
+
+TEST_SUITE(LexerTests) {
+}
diff --git a/test/main.c b/test/main.c
new file mode 100644 (file)
index 0000000..2ac4261
--- /dev/null
@@ -0,0 +1,9 @@
+#define INCLUDE_DEFS
+#include <atf.h>
+
+int main(int argc, char** argv) {
+    atf_init(argc,argv);
+    RUN_EXTERN_TEST_SUITE(LexerTests);
+    RUN_EXTERN_TEST_SUITE(ParserTests);
+    return atf_print_results();
+}
diff --git a/test/parser.c b/test/parser.c
new file mode 100644 (file)
index 0000000..92139b6
--- /dev/null
@@ -0,0 +1,26 @@
+#include <atf.h>
+#include "../src/parser.c"
+
+Parser TestCtx = {0};
+
+void lex_string(Parser* ctx, char* text) {
+    LexFile* file = calloc(sizeof(LexFile), 1u);
+    file->path = NULL;
+    file->fbeg = file->fpos = strdup(text);
+    file->next = ctx->file;
+    ctx->file = file;
+}
+
+int parse(char* text, AST* expect) {
+    memset(&TestCtx, 0, sizeof(TestCtx));
+    codegen_init(&TestCtx);
+    lex_string(&TestCtx, text);
+    AST* result = expression(&TestCtx);
+    return 1;
+}
+
+TEST_SUITE(ParserTests) {
+    TEST(should parse some stuff) {
+        CHECK(parse("123", NULL));
+    }
+}