diff --git a/.gitignore b/.gitignore index ed9c657..1b83f4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.o lake lake.a +test/* +!test/*.[ch] +!test/Makefile diff --git a/Makefile b/Makefile index 4206b2c..8c745f0 100644 --- a/Makefile +++ b/Makefile @@ -4,3 +4,12 @@ all: clean: cd src && make clean + +test: + cd src && make lake.a + cd test && make + +test_clean: + cd test && make clean + +.PHONY: all clean test test_clean diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..bc952dd --- /dev/null +++ b/test/Makefile @@ -0,0 +1,31 @@ +CC = gcc +CFLAGS := -Wall -g -I../src $(shell pkg-config --cflags glib-2.0) +LFLAGS := $(shell pkg-config --libs glib-2.0) +DEPS = minunit.h ../src/lake.a +TESTS = test_comment test_dlist test_env + +all: $(TESTS) + @clear + @for test in $(TESTS); do \ + echo; \ + ./$$test; \ + done + +test_comment: $(DEPS) test_comment.o + $(CC) $(CFLAGS) $(LFLAGS) $^ -o $@ + +test_dlist: $(DEPS) test_dlist.o + $(CC) $(CFLAGS) $(LFLAGS) $^ -o $@ + +test_env: $(DEPS) test_env.o + $(CC) $(CFLAGS) $(LFLAGS) $^ -o $@ + +test_eval: $(DEPS) test_eval.o + $(CC) $(CFLAGS) $(LFLAGS) $^ -o $@ + +# use touch to prevent errors in case files do not exist +clean: + @touch dummy.o $(TESTS) + rm -f *.o $(TESTS) + +.PHONY: all $(TESTS) diff --git a/test/minunit.h b/test/minunit.h new file mode 100644 index 0000000..9ae3aac --- /dev/null +++ b/test/minunit.h @@ -0,0 +1,18 @@ +#include + +#define mu_assert(message, test) do { \ + if (!(test)) { \ + fprintf(stderr, "%s:%d assertion failed\n", __FILE__, __LINE__); \ + return "error: " message; \ + } \ + } while (0) + +#define mu_run_test(test) do { \ + char *message = test(); \ + tests_run++; \ + if (message) { \ + return message; \ + } \ + } while (0) + +extern int tests_run; diff --git a/test/test_comment.c b/test/test_comment.c new file mode 100644 index 0000000..785be23 --- /dev/null +++ b/test/test_comment.c @@ -0,0 +1,89 @@ +/** + * test_comment.c + * Lake Scheme + * + * Copyright 2011 Sami Samhuri + * MIT License + * + */ + +#include +#include "minunit.h" +#include "comment.h" +#include "lake.h" +#include "str.h" + +#define TEXT "you are not expected to understand this" + +int tests_run; +static LakeStr *text = NULL; + +/* LakeComment *comment_make(LakeStr *text) */ +static char *test_comment_make(void) +{ + LakeComment *comment = comment_make(text); + mu_assert("type is not TYPE_COMM", IS(TYPE_COMM, comment)); + mu_assert("value size is incorrect", VAL_SIZE(comment) == sizeof(LakeComment)); + mu_assert("comment text is incorrect", str_equal(text, COMM_TEXT(comment))); + return 0; +} + +/* LakeComment *comment_from_c(char *text) */ +static char *test_comment_from_c(void) +{ + LakeComment *comment = comment_from_c(TEXT); + mu_assert("type is not TYPE_COMM", IS(TYPE_COMM, comment)); + mu_assert("value size is incorrect", VAL_SIZE(comment) == sizeof(LakeComment)); + mu_assert("comment text is incorrect", str_equal(text, COMM_TEXT(comment))); + return 0; +} + +/* char *comment_repr(LakeComment *comment) */ +static char *test_comment_repr(void) +{ + LakeComment *comment = comment_make(text); + mu_assert("comment_repr is incorrect", strncmp(comment_repr(comment), TEXT, strlen(TEXT)) == 0); + return 0; +} + +/* gboolean comment_equal(LakeComment *a, LakeComment *b) */ +static char *test_comment_equal(void) +{ + LakeComment *a = comment_make(text); + LakeComment *b = comment_from_c(TEXT); + LakeComment *c = comment_from_c("and now for something completely different"); + mu_assert("comment a != a", comment_equal(a, a)); + mu_assert("comment a != b", comment_equal(a, b)); + mu_assert("comment a == c", !comment_equal(a, c)); + mu_assert("comment b == c", !comment_equal(b, c)); + return 0; +} + +static void setup(void) +{ + text = str_from_c(TEXT); +} + +static char *all_tests() { + setup(); + mu_run_test(test_comment_make); + mu_run_test(test_comment_from_c); + mu_run_test(test_comment_repr); + mu_run_test(test_comment_equal); + return 0; +} + +int main(int argc, char const *argv[]) { + char *result = all_tests(); + int pass = result == 0; + printf("-- Comments --\n"); + if (pass) { + printf("PASS: %d test%s run\n", tests_run, tests_run == 1 ? "" : "s"); + } + else { + printf("%s\n", result); + printf("FAIL: %d test%s run\n", tests_run, tests_run == 1 ? "" : "s"); + } + + return !pass; +} diff --git a/test/test_dlist.c b/test/test_dlist.c new file mode 100644 index 0000000..530b31a --- /dev/null +++ b/test/test_dlist.c @@ -0,0 +1,100 @@ +/** + * test_dlist.c + * Lake Scheme + * + * Copyright 2011 Sami Samhuri + * MIT License + * + */ + +#include +#include +#include "minunit.h" +#include "lake.h" +#include "list.h" + +int tests_run; +static LakeList *head; +static LakeVal *tail; +static LakeDottedList *dlist; +static char *REPR = "(() . ())"; + +/* LakeDottedList *dlist_make(LakeList *head, LakeVal *tail) */ +static char *test_dlist_make(void) +{ + mu_assert("type is not TYPE_DLIST", IS(TYPE_DLIST, dlist)); + mu_assert("value size is incorrect", VAL_SIZE(dlist) == sizeof(LakeDottedList)); + mu_assert("head value is incorrect", + lake_equal(VAL(head), VAL(DLIST_HEAD(dlist)))); + mu_assert("tail value is incorrect", lake_equal(tail, DLIST_TAIL(dlist))); + return 0; +} + +/* char *dlist_repr(LakeDottedList *dlist) */ +static char *test_dlist_repr(void) +{ + mu_assert("dlist_repr is incorrect", strncmp(dlist_repr(dlist), REPR, strlen(REPR)) == 0); + + char *REPR2 = "(spam eggs bacon spam eggs . spam)"; + LakeCtx *lake = lake_init(); + LakeSym *s_spam = sym_intern(lake, "spam"); + LakeSym *s_eggs = sym_intern(lake, "eggs"); + LakeSym *s_bacon = sym_intern(lake, "bacon"); + LakeList *list = list_make(); + list_append(list, VAL(s_spam)); + list_append(list, VAL(s_eggs)); + list_append(list, VAL(s_bacon)); + list_append(list, VAL(s_spam)); + list_append(list, VAL(s_eggs)); + LakeDottedList *dlist2 = dlist_make(list, s_spam); + mu_assert("", strncmp(dlist_repr(dlist2), REPR2, strlen(REPR2)) == 0); + + return 0; +} + +/* gboolean dlist_equal(LakeDottedList *a, LakeDottedList *b) */ +static char *test_dlist_equal(void) +{ + LakeDottedList *a = dlist; + LakeDottedList *b = dlist_make(head, tail); + LakeVal *null = VAL(list_make()); + LakeList *null_pair = list_cons(null, null); + LakeDottedList *diff_head = dlist_make(null_pair, tail); + LakeDottedList *diff_tail = dlist_make(head, VAL(null_pair)); + mu_assert("dlist a != a", dlist_equal(a, a)); + mu_assert("dlist a != b", dlist_equal(a, b)); + mu_assert("dlist a == diff_head", !dlist_equal(a, diff_head)); + mu_assert("dlist a == diff_tail", !dlist_equal(a, diff_tail)); + return 0; +} + +static void setup(void) +{ + head = list_make(); + tail = VAL(list_make()); + dlist = dlist_make(head, tail); +} + +static char *all_tests() { + setup(); + mu_run_test(test_dlist_make); + mu_run_test(test_dlist_repr); + mu_run_test(test_dlist_equal); + return 0; +} + +int main(int argc, char const *argv[]) { + char *result = all_tests(); + int pass = result == 0; + printf("-- Dotted Lists --\n"); + if (pass) { + printf("PASS: %d test%s run\n", tests_run, tests_run == 1 ? "" : "s"); + } + else { + printf("%s\n", result); + printf("FAIL: %d test%s run\n", tests_run, tests_run == 1 ? "" : "s"); + } + + return !pass; +} + diff --git a/test/test_env.c b/test/test_env.c new file mode 100644 index 0000000..fd3ea52 --- /dev/null +++ b/test/test_env.c @@ -0,0 +1,143 @@ +/** + * test_env.c + * Lake Scheme + * + * Copyright 2011 Sami Samhuri + * MIT License + * + */ + +#include +#include "minunit.h" +#include "env.h" +#include "lake.h" + +int tests_run; + +static LakeCtx *lake; +static Env *toplevel; +static Env *firstlevel; +static LakeSym *s_answer; +static LakeVal *answer; +static LakeSym *s_undef; + +/* Env *env_make(Env *parent) */ +static char *test_env_make(void) +{ + mu_assert("toplevel is NULL", toplevel != NULL); + mu_assert("toplevel->parent is not NULL", toplevel->parent == NULL); + mu_assert("toplevel->bindings is NULL", toplevel->bindings != NULL); + + mu_assert("firstlevel is NULL", firstlevel != NULL); + mu_assert("firstlevel->parent is not toplevel", firstlevel->parent == toplevel); + + return 0; +} + +/* LakeVal *env_define(Env *env, LakeSym *key, LakeVal *val) */ +static char *test_env_define(void) +{ + env_define(toplevel, s_answer, answer); + mu_assert("env_define failed to define answer in toplevel", + toplevel == env_is_defined(toplevel, s_answer)); + mu_assert("env_define set the value incorrectly", + answer == env_get(toplevel, s_answer)); + return 0; +} + +/* Env *env_is_defined(Env *env, LakeSym *key) */ +static char *test_env_is_defined(void) +{ + /* unbound symbol */ + mu_assert("unbound symbol is defined", + env_is_defined(toplevel, s_undef) == NULL); + + /* symbol bound in env itself */ + env_define(toplevel, s_answer, answer); + mu_assert("failed to lookup symbol in defined env", + toplevel == env_is_defined(toplevel, s_answer)); + + /* symbol bound in parent */ + mu_assert("failed to lookup symbol in child env", + toplevel == env_is_defined(firstlevel, s_answer)); + + return 0; +} + +/* LakeVal *env_set(Env *env, LakeSym *key, LakeVal *val) */ +static char *test_env_set(void) +{ + /* unbound symbol */ + LakeVal *ret = env_set(toplevel, s_undef, answer); + mu_assert("env_set returned non-NULL for an unbound symbol", ret == NULL); + + /* symbol bound in env itself */ + env_define(toplevel, s_answer, answer); + LakeVal *zero = VAL(int_from_c(0)); + ret = env_set(toplevel, s_answer, zero); + mu_assert("env_set failed to set bound symbol", ret != NULL); + mu_assert("env_set failed to set bound symbol", + zero == env_get(toplevel, s_answer)); + + /* symbol bound in parent */ + ret = env_set(firstlevel, s_answer, answer); + mu_assert("env_set failed to set symbol bound in parent", ret != NULL); + mu_assert("env_set failed to set symbol bound in parent", + answer == env_get(toplevel, s_answer)); + + return 0; +} + +/* LakeVal *env_get(Env *env, LakeSym *key) */ +static char *test_env_get(void) +{ + /* unbound symbol */ + mu_assert("env_get returned non-NULL for an unbound symbol", + NULL == env_get(toplevel, s_undef)); + + /* symbol bound in env itself */ + env_define(toplevel, s_answer, answer); + mu_assert("failed to get value", answer == env_get(toplevel, s_answer)); + + /* symbol bound in parent */ + mu_assert("failed to get value defined in parent", + answer == env_get(firstlevel, s_answer)); + + return 0; +} + +static void setup(void) +{ + lake = lake_init(); + toplevel = lake->toplevel; + firstlevel = env_make(toplevel); + s_answer = sym_intern(lake, "answer"); + answer = VAL(int_from_c(42)); + s_undef = sym_intern(lake, "undefined"); +} + +static char *all_tests() { + setup(); + mu_run_test(test_env_make); + mu_run_test(test_env_define); + mu_run_test(test_env_set); + mu_run_test(test_env_get); + mu_run_test(test_env_is_defined); + return 0; +} + +int main(int argc, char const *argv[]) { + char *result = all_tests(); + int pass = result == 0; + printf("-- Environment --\n"); + if (pass) { + printf("PASS: %d test%s run\n", tests_run, tests_run == 1 ? "" : "s"); + } + else { + printf("%s\n", result); + printf("FAIL: %d test%s run\n", tests_run, tests_run == 1 ? "" : "s"); + } + + return !pass; +} +