From ea2f745f1e28404e9c6963382660ad2efeb4340a Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Wed, 4 Feb 2026 20:44:04 +0000 Subject: [PATCH] Split out tests a bit, made a stronger API for running the full test suite --- Makefile | 16 +-- test/data.h | 79 ++++++++++++++ test/main.c | 47 +++++++++ test/test.c | 244 ------------------------------------------- test/test.h | 18 ++++ test/test_lisp_api.c | 119 +++++++++++++++++++++ test/test_vec.c | 92 ++++++++++++++++ 7 files changed, 365 insertions(+), 250 deletions(-) create mode 100644 test/data.h create mode 100644 test/main.c delete mode 100644 test/test.c create mode 100644 test/test_lisp_api.c create mode 100644 test/test_vec.c diff --git a/Makefile b/Makefile index 8bba5b6..b8d5c6c 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CC=gcc +CC=cc DIST=build OUT=$(DIST)/alisp.out @@ -20,6 +20,9 @@ endif UNITS=src/sv.c src/vec.c src/stream.c src/symtable.c src/tag.c src/lisp.c OBJECTS:=$(patsubst src/%.c, $(DIST)/%.o, $(UNITS)) +TEST_UNITS=test/main.c +TEST_OBJECTS:=$(patsubst %.c, $(DIST)/%.o, $(TEST_UNITS)) + # Dependency generation DEPFLAGS=-MT $@ -MMD -MP -MF DEPDIR=$(DIST)/deps @@ -29,14 +32,14 @@ all: $(OUT) $(TEST) $(OUT): $(OBJECTS) $(DIST)/main.o | $(DIST) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) -$(TEST): $(OBJECTS) $(DIST)/test/test.o | $(DIST) - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) - $(DIST)/%.o: src/%.c | $(DIST) $(DEPDIR) $(CC) $(CFLAGS) $(DEPFLAGS) $(DEPDIR)/$*.d -c -o $@ $< +$(TEST): $(OBJECTS) $(TEST_OBJECTS) | $(DIST) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + $(DIST)/test/%.o: test/%.c | $(DIST) $(DEPDIR) - $(CC) $(CFLAGS) $(DEPFLAGS) $(DEPDIR)/$*.d -c -o $@ $< + $(CC) $(CFLAGS) $(DEPFLAGS) $(DEPDIR)/test/$*.d -c -o $@ $< $(DIST): mkdir -p $(DIST) @@ -44,6 +47,7 @@ $(DIST): $(DEPDIR): mkdir -p $(DEPDIR) + mkdir -p $(DEPDIR)/test clangd: compile_commands.json compile_commands.json: Makefile @@ -60,5 +64,5 @@ test: $(TEST) clean: rm -rf $(DIST) -DEPS:=$(patsubst src/%.c,$(DEPDIR)/%.d, $(UNITS)) $(DEPDIR)/main.d $(DEPDIR)/test.d +DEPS:=$(patsubst src/%.c,$(DEPDIR)/%.d, $(UNITS)) $(DEPDIR)/main.d $(DEPDIR)/test/main.d include $(wildcard $(DEPS)) diff --git a/test/data.h b/test/data.h new file mode 100644 index 0000000..2b69009 --- /dev/null +++ b/test/data.h @@ -0,0 +1,79 @@ +/* data.h: Sample data for testing. + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#ifndef DATA_H +#define DATA_H + +static const char *unique_words[] = { + "bibendum", "etiam", "gravida", "dui", "cursus", + "purus", "diam", "phasellus", "nam", "fermentum", + "leo", "enim", "ac", "semper", "non", + "mauris", "proin", "tellus", "vivamus", "lobortis", + "lacus", "neque", "in", "nullam", "felis", + "orci", "pede", "tempus", "nec", "at", + "tortor", "massa", "sed", "magna", "eget", + "tempor", "velit", "imperdiet", "praesent", "volutpat", + "tristique", "id", "commodo", "aliquet", "quis", + "pellentesque", "eleifend", "porta", "nunc", "euismod", + "aliquam", "a", "erat", "dignissim", "ut", + "vitae", "vel", "donec", +}; + +static const char *words[] = { + "aliquam", "erat", "volutpat", "nunc", "eleifend", + "leo", "vitae", "magna", "in", "id", + "erat", "non", "orci", "commodo", "lobortis", + "proin", "neque", "massa", "cursus", "ut", + "gravida", "ut", "lobortis", "eget", "lacus", + "sed", "diam", "praesent", "fermentum", "tempor", + "tellus", "nullam", "tempus", "mauris", "ac", + "felis", "vel", "velit", "tristique", "imperdiet", + "donec", "at", "pede", "etiam", "vel", + "neque", "nec", "dui", "dignissim", "bibendum", + "vivamus", "id", "enim", "phasellus", "neque", + "orci", "porta", "a", "aliquet", "quis", + "semper", "a", "massa", "phasellus", "purus", + "pellentesque", "tristique", "imperdiet", "tortor", "nam", + "euismod", "tellus", "id", "erat", +}; + +static const char words_text[] = + "aliquam erat volutpat nunc eleifend leo vitae magna in id erat non orci " + "commodo lobortis proin neque massa cursus ut gravida ut lobortis eget " + "lacus sed diam praesent fermentum tempor tellus nullam tempus mauris ac " + "felis vel velit tristique imperdiet donec at pede etiam vel neque nec dui " + "dignissim bibendum vivamus id enim phasellus neque orci porta a aliquet " + "quis semper a massa phasellus purus pellentesque tristique imperdiet " + "tortor nam euismod tellus id erat"; + +static const char text[] = + "Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. " + "Etiam vel tortor sodales tellus ultricies commodo. Suspendisse potenti. " + "Aenean in sem ac leo mollis blandit. Donec neque quam, dignissim in, " + "mollis nec, sagittis eu, wisi. Phasellus lacus. Etiam laoreet quam sed " + "arcu. Phasellus at dui in ligula mollis ultricies. Integer placerat " + "tristique nisl. Praesent augue. Fusce commodo. Vestibulum convallis, " + "lorem a tempus semper, dui dui euismod elit, vitae placerat urna tortor " + "vitae lacus. Nullam libero mauris, consequat quis, varius et, dictum id, " + "arcu. Mauris mollis tincidunt felis. Aliquam feugiat tellus ut neque. " + "Nulla facilisis, risus a rhoncus fermentum, tellus tellus lacinia purus, " + "et dictum nunc justo sit amet elit."; + +#endif + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Unlicense + * for details. + + * You may distribute and modify this code under the terms of the + * Unlicense, which you should have received a copy of along with this + * program. If not, please go to . + +*/ diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..b5ebffa --- /dev/null +++ b/test/main.c @@ -0,0 +1,47 @@ +/* main.c: Main boot file for unit tests + * Created: 2025-08-21 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#include +#include + +#include "./data.h" +#include "./test.h" + +#include "./test_lisp_api.c" +#include "./test_vec.c" + +test_suite_t SUITES[] = { + LISP_API_SUITE, + VEC_SUITE, +}; + +int main(void) +{ + for (u64 i = 0; i < ARRSIZE(SUITES); ++i) + { + test_suite_t suite = SUITES[i]; + printf("Suite [%s]\n", suite.name); + for (u64 j = 0; j < suite.size; ++j) + { + printf("[%s]: Running...\n", suite.tests[j].name); + suite.tests[j].fn(); + } + } + return 0; +} + +/* Copyright (C) 2025, 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to . + + */ diff --git a/test/test.c b/test/test.c deleted file mode 100644 index ce709a4..0000000 --- a/test/test.c +++ /dev/null @@ -1,244 +0,0 @@ -/* test.c: Tests - * Created: 2025-08-21 - * Author: Aryadev Chavali - * License: See end of file - * Commentary: - */ - -#include -#include - -#include "./test.h" - -// Sample data -const char *unique_words[] = { - "bibendum", "etiam", "gravida", "dui", "cursus", - "purus", "diam", "phasellus", "nam", "fermentum", - "leo", "enim", "ac", "semper", "non", - "mauris", "proin", "tellus", "vivamus", "lobortis", - "lacus", "neque", "in", "nullam", "felis", - "orci", "pede", "tempus", "nec", "at", - "tortor", "massa", "sed", "magna", "eget", - "tempor", "velit", "imperdiet", "praesent", "volutpat", - "tristique", "id", "commodo", "aliquet", "quis", - "pellentesque", "eleifend", "porta", "nunc", "euismod", - "aliquam", "a", "erat", "dignissim", "ut", - "vitae", "vel", "donec", -}; - -char *words[] = { - "aliquam", "erat", "volutpat", "nunc", "eleifend", - "leo", "vitae", "magna", "in", "id", - "erat", "non", "orci", "commodo", "lobortis", - "proin", "neque", "massa", "cursus", "ut", - "gravida", "ut", "lobortis", "eget", "lacus", - "sed", "diam", "praesent", "fermentum", "tempor", - "tellus", "nullam", "tempus", "mauris", "ac", - "felis", "vel", "velit", "tristique", "imperdiet", - "donec", "at", "pede", "etiam", "vel", - "neque", "nec", "dui", "dignissim", "bibendum", - "vivamus", "id", "enim", "phasellus", "neque", - "orci", "porta", "a", "aliquet", "quis", - "semper", "a", "massa", "phasellus", "purus", - "pellentesque", "tristique", "imperdiet", "tortor", "nam", - "euismod", "tellus", "id", "erat", -}; - -char words_text[] = - "aliquam erat volutpat nunc eleifend leo vitae magna in id erat non orci " - "commodo lobortis proin neque massa cursus ut gravida ut lobortis eget " - "lacus sed diam praesent fermentum tempor tellus nullam tempus mauris ac " - "felis vel velit tristique imperdiet donec at pede etiam vel neque nec dui " - "dignissim bibendum vivamus id enim phasellus neque orci porta a aliquet " - "quis semper a massa phasellus purus pellentesque tristique imperdiet " - "tortor nam euismod tellus id erat"; - -char text[] = - "Pellentesque dapibus suscipit ligula. Donec posuere augue in quam. " - "Etiam vel tortor sodales tellus ultricies commodo. Suspendisse potenti. " - "Aenean in sem ac leo mollis blandit. Donec neque quam, dignissim in, " - "mollis nec, sagittis eu, wisi. Phasellus lacus. Etiam laoreet quam sed " - "arcu. Phasellus at dui in ligula mollis ultricies. Integer placerat " - "tristique nisl. Praesent augue. Fusce commodo. Vestibulum convallis, " - "lorem a tempus semper, dui dui euismod elit, vitae placerat urna tortor " - "vitae lacus. Nullam libero mauris, consequat quis, varius et, dictum id, " - "arcu. Mauris mollis tincidunt felis. Aliquam feugiat tellus ut neque. " - "Nulla facilisis, risus a rhoncus fermentum, tellus tellus lacinia purus, " - "et dictum nunc justo sit amet elit."; - -void symtable_test(void) -{ - sym_table_t table = {0}; - sym_table_init(&table); - for (u64 i = 0; i < ARRSIZE(words); ++i) - sym_table_find(&table, SV(words[i], strlen(words[i]))); - - TEST(table.count == ARRSIZE(unique_words), "%lu == %lu", table.count, - ARRSIZE(unique_words)); - - TEST(table.count < ARRSIZE(unique_words), "%lu < %lu", table.count, - ARRSIZE(unique_words)); - - TEST_PASSED(); - - sym_table_cleanup(&table); -} - -void int_test(void) -{ - i64 ints[] = { - 1, -1, (1 << 10) - 1, (-1) * ((1 << 10) - 1), INT_MIN, INT_MAX, - }; - - for (u64 i = 0; i < ARRSIZE(ints); ++i) - { - i64 in = ints[i]; - lisp_t *lisp = make_int(in); - i64 out = as_int(lisp); - - TEST(in == out, "%ld == %ld", in, out); - } - - TEST_PASSED(); -} - -void sym_test(void) -{ - sys_t system = {0}; - sys_init(&system); - for (u64 i = 0; i < ARRSIZE(words); ++i) - { - char *in = words[i]; - lisp_t *lisp = intern(&system, SV(in, strlen(in))); - char *out = as_sym(lisp); - TEST(in != out, "%p != %p", in, out); - TEST(strlen(in) == strlen(out), "%zu == %zu", strlen(in), strlen(out)); - TEST(strncmp(in, out, strlen(in)) == 0, "%d", strncmp(in, out, strlen(in))); - } - TEST_PASSED(); - sys_cleanup(&system); -} - -void vec_test1(void) -{ - sys_t system = {0}; - sys_init(&system); - - // Generating a vector word by word - lisp_t *lvec = make_vec(&system, 0); - for (u64 i = 0; i < ARRSIZE(words); ++i) - { - char *word = words[i]; - vec_append(as_vec(lvec), word, strlen(word)); - if (i != ARRSIZE(words) - 1) - vec_append(as_vec(lvec), " ", 1); - } - vec_append(as_vec(lvec), "\0", 1); - - vec_t *vec = as_vec(lvec); - - TEST(vec->size == ARRSIZE(words_text), "%lu == %lu", vec->size, - ARRSIZE(words_text)); - TEST(strncmp((char *)vec_data(vec), words_text, vec->size) == 0, "%d", - strncmp((char *)vec_data(vec), words_text, vec->size)); - - TEST_PASSED(); - sys_cleanup(&system); -} - -void vec_test2(void) -{ - sys_t system = {0}; - sys_init(&system); - // Generating substrings - struct Test - { - u64 start, size; - } tests[] = { - {0, 16}, - {0, 32}, - {32, 64}, - {0, ARRSIZE(text)}, - }; - - for (u64 i = 0; i < ARRSIZE(tests); ++i) - { - struct Test test = tests[i]; - const sv_t substr = SV(text + test.start, test.size); - const u64 size = test.size / 2; - - lisp_t *lvec = make_vec(&system, size); - vec_append(as_vec(lvec), text + test.start, test.size); - TEST(as_vec(lvec)->size > size, "%lu > %lu", as_vec(lvec)->size, size); - TEST(strncmp((char *)vec_data(as_vec(lvec)), substr.data, substr.size) == 0, - "%d", - strncmp((char *)vec_data(as_vec(lvec)), substr.data, substr.size)); - } - - TEST_PASSED(); - sys_cleanup(&system); -} - -void cons_test(void) -{ - sys_t system = {0}; - sys_init(&system); - - // Let's make a list of words using `cons` - lisp_t *lisp = NIL; - for (u64 i = 0; i < ARRSIZE(words); ++i) - { - char *word = words[i]; - lisp_t *lword = intern(&system, SV(word, strlen(word))); - lisp = cons(&system, lword, lisp); - } - - // Make sure we've essentially reversed the `words` array - u64 i = ARRSIZE(words); - for (lisp_t *iter = lisp; iter; iter = cdr(iter)) - { - lisp_t *item = car(iter); - TEST(strncmp(words[i - 1], as_sym(item), strlen(words[i - 1])) == 0, "%d", - strncmp(words[i - 1], as_sym(item), strlen(words[i - 1]))); - i -= 1; - } - - TEST_PASSED(); - - sys_cleanup(&system); -} - -struct TestFn -{ - const char *name; - void (*fn)(void); -}; - -#define MAKE_TEST_FN(NAME) {.name = #NAME, .fn = NAME} - -const struct TestFn TESTS[] = { - MAKE_TEST_FN(int_test), MAKE_TEST_FN(sym_test), MAKE_TEST_FN(vec_test1), - MAKE_TEST_FN(vec_test2), MAKE_TEST_FN(cons_test), -}; - -int main(void) -{ - for (u64 i = 0; i < ARRSIZE(TESTS); ++i) - { - printf("[%s]: Running...\n", TESTS[i].name); - TESTS[i].fn(); - } - return 0; -} - -/* Copyright (C) 2025, 2026 Aryadev Chavali - - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the Unlicense for details. - - * You may distribute and modify this code under the terms of the Unlicense, - * which you should have received a copy of along with this program. If not, - * please go to . - - */ diff --git a/test/test.h b/test/test.h index f9a5bec..c887e1f 100644 --- a/test/test.h +++ b/test/test.h @@ -32,6 +32,24 @@ } \ } while (0) +typedef struct TestFn +{ + const char *name; + void (*fn)(void); +} test_fn; + +#define MAKE_TEST_FN(NAME) {.name = #NAME, .fn = NAME} + +typedef struct +{ + const char *name; + const test_fn *tests; + const u64 size; +} test_suite_t; + +#define MAKE_TEST_SUITE(NAME) \ + {.name = #NAME, .tests = NAME, .size = ARRSIZE(NAME)} + #endif /* Copyright (C) 2026 Aryadev Chavali diff --git a/test/test_lisp_api.c b/test/test_lisp_api.c new file mode 100644 index 0000000..4d945dc --- /dev/null +++ b/test/test_lisp_api.c @@ -0,0 +1,119 @@ +/* test_lisp_api.c: Testing of constructors/destructors in the Lisp system + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#include "./data.h" +#include "./test.h" + +#include + +void int_test(void) +{ + i64 ints[] = { + 1, -1, (1 << 10) - 1, (-1) * ((1 << 10) - 1), INT_MIN, INT_MAX, + }; + + for (u64 i = 0; i < ARRSIZE(ints); ++i) + { + i64 in = ints[i]; + lisp_t *lisp = make_int(in); + i64 out = as_int(lisp); + + TEST(in == out, "%ld == %ld", in, out); + } + + TEST_PASSED(); +} + +void symtable_test(void) +{ + sym_table_t table = {0}; + sym_table_init(&table); + for (u64 i = 0; i < ARRSIZE(words); ++i) + sym_table_find(&table, SV((char *)words[i], strlen(words[i]))); + + TEST(table.count == ARRSIZE(unique_words), "%lu == %lu", table.count, + ARRSIZE(unique_words)); + + TEST(table.count < ARRSIZE(unique_words), "%lu < %lu", table.count, + ARRSIZE(unique_words)); + + TEST_PASSED(); + + sym_table_cleanup(&table); +} + +void sym_test(void) +{ + sys_t system = {0}; + sys_init(&system); + for (u64 i = 0; i < ARRSIZE(words); ++i) + { + const char *in = words[i]; + lisp_t *lisp = intern(&system, SV((char *)in, strlen(in))); + char *out = as_sym(lisp); + TEST(in != out, "%p != %p", in, out); + TEST(strlen(in) == strlen(out), "%zu == %zu", strlen(in), strlen(out)); + TEST(strncmp(in, out, strlen(in)) == 0, "%d", strncmp(in, out, strlen(in))); + } + TEST_PASSED(); + sys_cleanup(&system); +} + +void cons_test(void) +{ + sys_t system = {0}; + sys_init(&system); + + // Let's make a list of words using `cons` + lisp_t *lisp = NIL; + for (u64 i = 0; i < ARRSIZE(words); ++i) + { + const char *word = words[i]; + lisp_t *lword = intern(&system, SV((char *)word, strlen(word))); + lisp = cons(&system, lword, lisp); + } + + // Make sure we've essentially reversed the `words` array + u64 i = ARRSIZE(words); + for (lisp_t *iter = lisp; iter; iter = cdr(iter), --i) + { + const char *expected = words[i - 1]; + lisp_t *item = car(iter); + char *got = as_sym(item); + size_t size = MIN(strlen(expected), strlen(got)); + + TEST(strncmp(expected, got, size) == 0, "%s == %s", expected, got); + } + + TEST_PASSED(); + + sys_cleanup(&system); +} + +const test_fn TESTS_LISP_API[] = { + MAKE_TEST_FN(int_test), + MAKE_TEST_FN(sym_test), + MAKE_TEST_FN(cons_test), +}; + +const test_suite_t LISP_API_SUITE = { + .name = "Lisp API Tests", + .tests = TESTS_LISP_API, + .size = ARRSIZE(TESTS_LISP_API), +}; + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to . + + */ diff --git a/test/test_vec.c b/test/test_vec.c new file mode 100644 index 0000000..08b24a0 --- /dev/null +++ b/test/test_vec.c @@ -0,0 +1,92 @@ +/* test_vec.c: Vector tests + * Created: 2026-02-04 + * Author: Aryadev Chavali + * License: See end of file + * Commentary: + */ + +#include "./data.h" +#include "./test.h" + +void vec_test1(void) +{ + sys_t system = {0}; + sys_init(&system); + + // Generating a vector word by word + lisp_t *lvec = make_vec(&system, 0); + for (u64 i = 0; i < ARRSIZE(words); ++i) + { + const char *word = words[i]; + vec_append(as_vec(lvec), word, strlen(word)); + if (i != ARRSIZE(words) - 1) + vec_append(as_vec(lvec), " ", 1); + } + vec_append(as_vec(lvec), "\0", 1); + + vec_t *vec = as_vec(lvec); + + TEST(vec->size == ARRSIZE(words_text), "%lu == %lu", vec->size, + ARRSIZE(words_text)); + TEST(strncmp((char *)vec_data(vec), words_text, vec->size) == 0, "%d", + strncmp((char *)vec_data(vec), words_text, vec->size)); + + TEST_PASSED(); + sys_cleanup(&system); +} + +void vec_test2(void) +{ + sys_t system = {0}; + sys_init(&system); + // Generating substrings + struct Test + { + u64 start, size; + } tests[] = { + {0, 16}, + {0, 32}, + {32, 64}, + {0, ARRSIZE(text)}, + }; + + for (u64 i = 0; i < ARRSIZE(tests); ++i) + { + struct Test test = tests[i]; + const sv_t substr = SV((char *)text + test.start, test.size); + const u64 size = test.size / 2; + + lisp_t *lvec = make_vec(&system, size); + vec_append(as_vec(lvec), text + test.start, test.size); + TEST(as_vec(lvec)->size > size, "%lu > %lu", as_vec(lvec)->size, size); + TEST(strncmp((char *)vec_data(as_vec(lvec)), substr.data, substr.size) == 0, + "%d", + strncmp((char *)vec_data(as_vec(lvec)), substr.data, substr.size)); + } + + TEST_PASSED(); + sys_cleanup(&system); +} + +const test_fn TESTS_VEC[] = { + MAKE_TEST_FN(vec_test1), + MAKE_TEST_FN(vec_test2), +}; + +const test_suite_t VEC_SUITE = { + .name = "Vector Tests", + .tests = TESTS_VEC, + .size = ARRSIZE(TESTS_VEC), +}; + +/* Copyright (C) 2026 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to . + + */