From: Michael D. Lowis Date: Sun, 31 Mar 2019 03:35:12 +0000 (-0400) Subject: setup test framework and added first test X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=921aca566ce39b3b097eea932c717a218f63b656;p=proto%2Fsclpl.git setup test framework and added first test --- diff --git a/.gitignore b/.gitignore index c85b455..43e96d0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ cscope.out sclpl source/lexer.c project.vim +tests diff --git a/Makefile b/Makefile index 4e32579..2f25fb5 100644 --- 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 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 +#include +#include +#include + +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 +#include +#ifndef NO_SIGNALS +#include +#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 diff --git a/src/lex.c b/src/lex.c index 1351a98..eaf89fc 100644 --- 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 index 0000000..bdde76a --- /dev/null +++ b/test/lex.c @@ -0,0 +1,4 @@ +#include + +TEST_SUITE(LexerTests) { +} diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..2ac4261 --- /dev/null +++ b/test/main.c @@ -0,0 +1,9 @@ +#define INCLUDE_DEFS +#include + +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 index 0000000..92139b6 --- /dev/null +++ b/test/parser.c @@ -0,0 +1,26 @@ +#include +#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)); + } +}