From fbb7e1bc54106a3121f71432ff824bddc962c22b Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 5 Apr 2026 18:42:56 +1000 Subject: [PATCH] WIP: Retreived the unit tests (formerly test cases) Some of these still work, others have just been dummied out for now. Also added tests for console colors tool, and tweaked it to work properly. --- makefile | 13 +- repl/makefile | 2 +- source/toy_console_colors.h | 74 ++-- tests/README.md | 3 + tests/makefile | 6 + tests/units/gdb_init | 1 + tests/units/makefile | 86 +++++ tests/units/test_array.c | 52 +++ tests/units/test_ast.c | 9 + tests/units/test_bucket.c | 89 +++++ tests/units/test_compiler.c | 9 + tests/units/test_lexer.c | 47 +++ tests/units/test_parser.c | 9 + tests/units/test_print.c | 167 +++++++++ tests/units/test_scope.c | 9 + tests/units/test_stack.c | 150 +++++++++ tests/units/test_string.c | 9 + tests/units/test_table.c | 653 ++++++++++++++++++++++++++++++++++++ tests/units/test_value.c | 9 + tests/units/test_vm.c | 9 + 20 files changed, 1366 insertions(+), 40 deletions(-) create mode 100644 tests/README.md create mode 100644 tests/makefile create mode 100644 tests/units/gdb_init create mode 100644 tests/units/makefile create mode 100644 tests/units/test_array.c create mode 100644 tests/units/test_ast.c create mode 100644 tests/units/test_bucket.c create mode 100644 tests/units/test_compiler.c create mode 100644 tests/units/test_lexer.c create mode 100644 tests/units/test_parser.c create mode 100644 tests/units/test_print.c create mode 100644 tests/units/test_scope.c create mode 100644 tests/units/test_stack.c create mode 100644 tests/units/test_string.c create mode 100644 tests/units/test_table.c create mode 100644 tests/units/test_value.c create mode 100644 tests/units/test_vm.c diff --git a/makefile b/makefile index 441b917..c239090 100644 --- a/makefile +++ b/makefile @@ -23,6 +23,10 @@ source: repl: source $(MAKE) -C repl -k +.PHONY: tests +tests: + $(MAKE) -C tests -k + #util targets $(TOY_OUTDIR): mkdir $(TOY_OUTDIR) @@ -34,38 +38,41 @@ $(TOY_OBJDIR): .PHONY: clean clean: ifeq ($(shell uname),Linux) - rm -r out find . -type f -name '*.o' -delete find . -type f -name '*.a' -delete + find . -type f -name '*.out' -delete find . -type f -name '*.exe' -delete find . -type f -name '*.dll' -delete find . -type f -name '*.lib' -delete find . -type f -name '*.so' -delete find . -type f -name '*.dylib' -delete + find . -type d -name 'out' -delete find . -type d -name 'obj' -delete else ifeq ($(shell uname),NetBSD) - rm -r out find . -type f -name '*.o' -delete find . -type f -name '*.a' -delete + find . -type f -name '*.out' -delete find . -type f -name '*.exe' -delete find . -type f -name '*.dll' -delete find . -type f -name '*.lib' -delete find . -type f -name '*.so' -delete find . -type f -name '*.dylib' -delete + find . -type d -name 'out' -delete find . -type d -name 'obj' -delete else ifeq ($(OS),Windows_NT) $(RM) *.o *.a *.exe *.dll *.lib *.so *.dylib $(RM) out $(RM) obj else ifeq ($(shell uname),Darwin) - rm -r out find . -type f -name '*.o' -delete find . -type f -name '*.a' -delete + find . -type f -name '*.out' -delete find . -type f -name '*.exe' -delete find . -type f -name '*.dll' -delete find . -type f -name '*.lib' -delete find . -type f -name '*.so' -delete find . -type f -name '*.dylib' -delete + find . -type d -name 'out' -delete find . -type d -name 'obj' -delete else @echo "Deletion failed - what platform is this?" diff --git a/repl/makefile b/repl/makefile index 7d76da1..e86a2eb 100644 --- a/repl/makefile +++ b/repl/makefile @@ -21,7 +21,7 @@ REPL_TARGETNAME=repl ifeq ($(OS),Windows_NT) REPL_TARGETEXT=.exe else - REPL_TARGETEXT= + REPL_TARGETEXT=.out endif #linker fix diff --git a/source/toy_console_colors.h b/source/toy_console_colors.h index 065f60b..419cbc6 100644 --- a/source/toy_console_colors.h +++ b/source/toy_console_colors.h @@ -8,41 +8,42 @@ a printf()'s first argument, like so: printf(TOY_CC_NOTICE "Hello world" TOY_CC_RESET); -NOTE: you need both font AND background for these to work +reference: https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences */ //platform/compiler-specific instructions -#if defined(TOY_CC_ENABLED) && ( defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__) ) +#if defined(__linux__) || defined(__MINGW32__) || defined(__GNUC__) //fonts color -#define TOY_CC_FONT_BLACK "\033[30;" -#define TOY_CC_FONT_RED "\033[31;" -#define TOY_CC_FONT_GREEN "\033[32;" -#define TOY_CC_FONT_YELLOW "\033[33;" -#define TOY_CC_FONT_BLUE "\033[34;" -#define TOY_CC_FONT_PURPLE "\033[35;" -#define TOY_CC_FONT_DGREEN "\033[6;" -#define TOY_CC_FONT_WHITE "\033[7;" -#define TOY_CC_FONT_CYAN "\x1b[36m" +#define TOY_CC_FONT_BLACK "30" +#define TOY_CC_FONT_RED "31" +#define TOY_CC_FONT_GREEN "32" +#define TOY_CC_FONT_YELLOW "33" +#define TOY_CC_FONT_BLUE "34" +#define TOY_CC_FONT_MAGENTA "35" +#define TOY_CC_FONT_CYAN "36" +#define TOY_CC_FONT_WHITE "37" +#define TOY_CC_FONT_DEFAULT "39" //background color -#define TOY_CC_BACK_BLACK "40m" -#define TOY_CC_BACK_RED "41m" -#define TOY_CC_BACK_GREEN "42m" -#define TOY_CC_BACK_YELLOW "43m" -#define TOY_CC_BACK_BLUE "44m" -#define TOY_CC_BACK_PURPLE "45m" -#define TOY_CC_BACK_DGREEN "46m" -#define TOY_CC_BACK_WHITE "47m" +#define TOY_CC_BACK_BLACK "40" +#define TOY_CC_BACK_RED "41" +#define TOY_CC_BACK_GREEN "42" +#define TOY_CC_BACK_YELLOW "43" +#define TOY_CC_BACK_BLUE "44" +#define TOY_CC_BACK_MAGENTA "45" +#define TOY_CC_BACK_CYAN "46" +#define TOY_CC_BACK_WHITE "47" +#define TOY_CC_BACK_DEFAULT "49" -//useful -#define TOY_CC_DEBUG TOY_CC_FONT_BLUE TOY_CC_BACK_BLACK -#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK -#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK -#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK -#define TOY_CC_ASSERT TOY_CC_FONT_PURPLE TOY_CC_BACK_BLACK -#define TOY_CC_RESET "\033[0m" +//useful macros +#define TOY_CC_DEBUG "\033[" TOY_CC_FONT_BLUE ";" TOY_CC_BACK_DEFAULT "m" +#define TOY_CC_NOTICE "\033[" TOY_CC_FONT_GREEN ";" TOY_CC_BACK_DEFAULT "m" +#define TOY_CC_WARN "\033[" TOY_CC_FONT_YELLOW ";" TOY_CC_BACK_DEFAULT "m" +#define TOY_CC_ERROR "\033[" TOY_CC_FONT_RED ";" TOY_CC_BACK_DEFAULT "m" +#define TOY_CC_ASSERT "\033[" TOY_CC_FONT_BLACK ";" TOY_CC_BACK_MAGENTA "m" +#define TOY_CC_RESET "\033[" "0" "m" //for unsupported platforms, these become no-ops #else @@ -53,10 +54,10 @@ NOTE: you need both font AND background for these to work #define TOY_CC_FONT_GREEN #define TOY_CC_FONT_YELLOW #define TOY_CC_FONT_BLUE -#define TOY_CC_FONT_PURPLE -#define TOY_CC_FONT_DGREEN -#define TOY_CC_FONT_WHITE +#define TOY_CC_FONT_MAGENTA #define TOY_CC_FONT_CYAN +#define TOY_CC_FONT_WHITE +#define TOY_CC_FONT_DEFAULT //background color #define TOY_CC_BACK_BLACK @@ -64,16 +65,17 @@ NOTE: you need both font AND background for these to work #define TOY_CC_BACK_GREEN #define TOY_CC_BACK_YELLOW #define TOY_CC_BACK_BLUE -#define TOY_CC_BACK_PURPLE -#define TOY_CC_BACK_DGREEN +#define TOY_CC_BACK_MAGENTA +#define TOY_CC_BACK_CYAN #define TOY_CC_BACK_WHITE +#define TOY_CC_BACK_DEFAULT //useful -#define TOY_CC_DEBUG TOY_CC_FONT_BLUE TOY_CC_BACK_BLACK -#define TOY_CC_NOTICE TOY_CC_FONT_GREEN TOY_CC_BACK_BLACK -#define TOY_CC_WARN TOY_CC_FONT_YELLOW TOY_CC_BACK_BLACK -#define TOY_CC_ERROR TOY_CC_FONT_RED TOY_CC_BACK_BLACK -#define TOY_CC_ASSERT TOY_CC_FONT_PURPLE TOY_CC_BACK_BLACK +#define TOY_CC_DEBUG +#define TOY_CC_NOTICE +#define TOY_CC_WARN +#define TOY_CC_ERROR +#define TOY_CC_ASSERT #define TOY_CC_RESET #endif diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..7f87eb2 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,3 @@ +# Tests + +* units - for checking the functionality of a single unit of code \ No newline at end of file diff --git a/tests/makefile b/tests/makefile new file mode 100644 index 0000000..8ac2fad --- /dev/null +++ b/tests/makefile @@ -0,0 +1,6 @@ +#bridge file + +export CFLAGS+=-DTOY_CC_ENABLED + +all: + $(MAKE) -C units -k \ No newline at end of file diff --git a/tests/units/gdb_init b/tests/units/gdb_init new file mode 100644 index 0000000..043b1d8 --- /dev/null +++ b/tests/units/gdb_init @@ -0,0 +1 @@ +set breakpoint pending on diff --git a/tests/units/makefile b/tests/units/makefile new file mode 100644 index 0000000..4715f35 --- /dev/null +++ b/tests/units/makefile @@ -0,0 +1,86 @@ +#compiler settings +CC=gcc +CFLAGS+=-std=c17 -g -Wall -Werror -Wextra -Wpedantic -Wformat=2 -Wno-newline-eof +LIBS+=-lm +LDFLAGS+= + +ifeq ($(shell uname),Linux) +LDFLAGS=-Wl,--gc-sections +else ifeq ($(shell uname),NetBSD) +LDFLAGS=-Wl,--gc-sections +else ifeq ($(OS),Windows_NT) +LDFLAGS=-Wl,--gc-sections +else ifeq ($(shell uname),Darwin) +LDFLAGS=-Wl,-dead_strip +else + @echo "LDFLAGS set failed - what platform is this?" +endif + +#directories +TEST_ROOTDIR=../.. +TEST_SOURCEDIR=$(TEST_ROOTDIR)/$(TOY_SOURCEDIR) +TEST_CASESDIR=. + +TEST_OUTDIR=out +TEST_OBJDIR=obj + +#file names +TEST_SOURCEFILES=$(wildcard $(TEST_SOURCEDIR)/*.c) +TEST_CASESFILES=$(wildcard $(TEST_CASESDIR)/test_*.c) + +#build the object files, compile the test cases, and run +all: build-source build-cases build-link build-run + +#targets for each step +.PHONY: build-source +build-source: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) + +.PHONY: build-cases +build-cases: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_CASESFILES:.c=.o))) + +.PHONY: build-link +build-link: $(TEST_OUTDIR) $(TEST_OBJDIR) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) + +.PHONY: build-run +build-run: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run))) + +#util targets +$(TEST_OUTDIR): + mkdir $(TEST_OUTDIR) + +$(TEST_OBJDIR): + mkdir $(TEST_OBJDIR) + +#compilation steps +$(TEST_OBJDIR)/%.o: $(TEST_SOURCEDIR)/%.c + $(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR)) $(CFLAGS) -fdata-sections -ffunction-sections + +$(TEST_OBJDIR)/%.o: $(TEST_CASESDIR)/%.c + $(CC) -c -o $@ $< $(addprefix -I,$(TEST_SOURCEDIR) $(TEST_CASESDIR)) $(CFLAGS) -fdata-sections -ffunction-sections + +$(TEST_OUTDIR)/%.exe: $(TEST_OBJDIR)/%.o + @$(CC) -o $@ $< $(addprefix $(TEST_OBJDIR)/,$(notdir $(TEST_SOURCEFILES:.c=.o))) $(CFLAGS) $(LIBS) $(LDFLAGS) + +.PRECIOUS: $(TEST_OUTDIR)/%.run +$(TEST_OUTDIR)/%.run: $(TEST_OUTDIR)/%.exe + $< + +#debugging targets +gdb: build-source build-cases build-link build-run-gdb + +.PHONY: build-run-gdb +build-run-gdb: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run-gdb))) + +.PRECIOUS: $(TEST_OUTDIR)/%.run-gdb +$(TEST_OUTDIR)/%.run-gdb: $(TEST_OUTDIR)/%.exe + gdb $< -ix gdb_init -ex=run --batch --return-child-result --args "$<" + +#valgrind targets +valgrind: build-source build-cases build-link build-run-valgrind + +.PHONY: build-run-valgrind +build-run-valgrind: $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.exe))) $(addprefix $(TEST_OUTDIR)/,$(notdir $(TEST_CASESFILES:%.c=%.run-valgrind))) + +.PRECIOUS: $(TEST_OUTDIR)/%.run-valgrind +$(TEST_OUTDIR)/%.run-valgrind: $(TEST_OUTDIR)/%.exe + valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes $< \ No newline at end of file diff --git a/tests/units/test_array.c b/tests/units/test_array.c new file mode 100644 index 0000000..28ab371 --- /dev/null +++ b/tests/units/test_array.c @@ -0,0 +1,52 @@ +#include "toy_array.h" +#include "toy_console_colors.h" + +#include + +int test_array(void) { + //test allocation and free + { + Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); + array = Toy_resizeArray(array, 0); + } + + //test initial data + { + Toy_Array* array = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); + + //check you can access the memory + array->data[1] = TOY_VALUE_FROM_INTEGER(42); + + array = Toy_resizeArray(array, 0); + } + + //test multiple arrays (no overlaps or conflicts) + { + Toy_Array* array1 = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); + Toy_Array* array2 = Toy_resizeArray(NULL, TOY_ARRAY_INITIAL_CAPACITY); + + array1->data[1] = TOY_VALUE_FROM_INTEGER(42); + array2->data[1] = TOY_VALUE_FROM_INTEGER(42); + + Toy_resizeArray(array1, 0); + Toy_resizeArray(array2, 0); + } + + return 0; +} + +int main(void) { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_array(); + total += res; + + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + } + + return total; +} diff --git a/tests/units/test_ast.c b/tests/units/test_ast.c new file mode 100644 index 0000000..934dfe3 --- /dev/null +++ b/tests/units/test_ast.c @@ -0,0 +1,9 @@ +#include "toy_ast.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +} diff --git a/tests/units/test_bucket.c b/tests/units/test_bucket.c new file mode 100644 index 0000000..2785d9e --- /dev/null +++ b/tests/units/test_bucket.c @@ -0,0 +1,89 @@ +#include "toy_bucket.h" +#include "toy_console_colors.h" + +#include + +int test_buckets(void) { + //test initializing and freeing a bucket + { + //init + Toy_Bucket* bucket = Toy_allocateBucket(sizeof(int) * 32); + + //check + if (bucket == NULL || bucket->capacity != 32 * sizeof(int)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to initialize 'Toy_Bucket'\n" TOY_CC_RESET); + return -1; + } + + //cleanup + Toy_freeBucket(&bucket); + } + + //test partitioning a bucket, several times + { + //init + Toy_Bucket* bucket = Toy_allocateBucket(sizeof(int) * 32); + + //grab some memory + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + + //check + if (bucket == NULL || bucket->count != 4 * sizeof(int)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to partition 'Toy_Bucket' correctly: count is %d, expected %d\n" TOY_CC_RESET, (int)(bucket->count), (int)(4*sizeof(int))); + return -1; + } + + //cleanup + Toy_freeBucket(&bucket); + } + + //test partitioning a bucket, several times, with an internal expansion + { + //init + Toy_Bucket* bucket = Toy_allocateBucket(sizeof(int) * 4); + + //grab some memory + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + Toy_partitionBucket(&bucket, sizeof(int)); + + //checks - please note that the top-most bucket is what is being filled - older buckets are further along + if ( + bucket->capacity != 4 * sizeof(int) || + bucket->count != 2 * sizeof(int) || + bucket->next == NULL || + bucket->next->capacity != 4 * sizeof(int) || + bucket->next->count != 4 * sizeof(int)) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to expand 'Toy_Bucket' correctly\n" TOY_CC_RESET); + return -1; + } + + //cleanup + Toy_freeBucket(&bucket); + } + + return 0; +} + +int main(void) { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_buckets(); + total += res; + + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + } + + return total; +} diff --git a/tests/units/test_compiler.c b/tests/units/test_compiler.c new file mode 100644 index 0000000..1c097c4 --- /dev/null +++ b/tests/units/test_compiler.c @@ -0,0 +1,9 @@ +#include "toy_compiler.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +} diff --git a/tests/units/test_lexer.c b/tests/units/test_lexer.c new file mode 100644 index 0000000..845057f --- /dev/null +++ b/tests/units/test_lexer.c @@ -0,0 +1,47 @@ +#include "toy_lexer.h" +#include "toy_console_colors.h" + +#include +#include + +int main(void) { + { + //source code sample to operate on + char* source = "print null;"; + + //test the lexer + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + //get each token + Toy_Token print = Toy_private_scanLexer(&lexer); + Toy_Token null = Toy_private_scanLexer(&lexer); + Toy_Token semi = Toy_private_scanLexer(&lexer); + Toy_Token eof = Toy_private_scanLexer(&lexer); + + //test each token is correct + if (strncmp(print.lexeme, "print", print.length)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: print lexeme is wrong: %s" TOY_CC_RESET, print.lexeme); + return -1; + } + + + if (strncmp(null.lexeme, "null", null.length)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: null lexeme is wrong: %s" TOY_CC_RESET, null.lexeme); + return -1; + } + + if (strncmp(semi.lexeme, ";", semi.length)) { + fprintf(stderr, TOY_CC_ERROR "ERROR: semicolon lexeme is wrong: %s" TOY_CC_RESET, semi.lexeme); + return -1; + } + + if (eof.type != TOY_TOKEN_EOF) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to find EOF token" TOY_CC_RESET); + return -1; + } + } + + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + return 0; +} diff --git a/tests/units/test_parser.c b/tests/units/test_parser.c new file mode 100644 index 0000000..d2f40ce --- /dev/null +++ b/tests/units/test_parser.c @@ -0,0 +1,9 @@ +#include "toy_parser.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +} diff --git a/tests/units/test_print.c b/tests/units/test_print.c new file mode 100644 index 0000000..ccca559 --- /dev/null +++ b/tests/units/test_print.c @@ -0,0 +1,167 @@ +#include "toy_print.h" +#include "toy_console_colors.h" + +#include + +int counter = 0; + +void counterCallback(const char* msg) { + (void)msg; + counter++; +} + +int test_callbacks(void) { + //set a custom print callback, invoke it, and reset + { + //setup + Toy_setPrintCallback(counterCallback); + + //invoke + Toy_print(""); + + //check + if (counter != 1) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to set print callback\n" TOY_CC_RESET); + return -1; + } + + //reset and retry + Toy_resetPrintCallback(); + Toy_print(""); + + if (counter != 1) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reset print callback\n" TOY_CC_RESET); + return -1; + } + + //cleanup + counter = 0; + } + + //set a custom error callback, invoke it, and reset + { + //setup + Toy_setErrorCallback(counterCallback); + + //invoke + Toy_error(""); + + //check + if (counter != 1) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to set error callback\n" TOY_CC_RESET); + return -1; + } + + //reset and retry + Toy_resetErrorCallback(); + Toy_error(""); + + if (counter != 1) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reset error callback\n" TOY_CC_RESET); + return -1; + } + + //cleanup + counter = 0; + } + + //set a custom assert failure callback, invoke it, and reset + { + //setup + Toy_setAssertFailureCallback(counterCallback); + + //invoke + Toy_assertFailure(""); + + //check + if (counter != 1) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to set assert failure callback\n" TOY_CC_RESET); + return -1; + } + + //reset and retry + Toy_resetAssertFailureCallback(); + Toy_assertFailure(""); + + if (counter != 1) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to reset assert failure callback\n" TOY_CC_RESET); + return -1; + } + + //cleanup + counter = 0; + } + + return 0; +} + +int test_console_colors(void) { + //test each font color + { +#define TEST_FONT(value) printf("\033[" value "m" #value TOY_CC_RESET "\n") + TEST_FONT(TOY_CC_FONT_BLACK); + TEST_FONT(TOY_CC_FONT_RED); + TEST_FONT(TOY_CC_FONT_GREEN); + TEST_FONT(TOY_CC_FONT_YELLOW); + TEST_FONT(TOY_CC_FONT_BLUE); + TEST_FONT(TOY_CC_FONT_MAGENTA); + TEST_FONT(TOY_CC_FONT_CYAN); + TEST_FONT(TOY_CC_FONT_WHITE); + TEST_FONT(TOY_CC_FONT_DEFAULT); +#undef TEST_FONT + } + + //test each background color + { +#define TEST_BACK(value) printf("\033[" value "m" #value TOY_CC_RESET "\n") + TEST_BACK(TOY_CC_BACK_BLACK); + TEST_BACK(TOY_CC_BACK_RED); + TEST_BACK(TOY_CC_BACK_GREEN); + TEST_BACK(TOY_CC_BACK_YELLOW); + TEST_BACK(TOY_CC_BACK_BLUE); + TEST_BACK(TOY_CC_BACK_MAGENTA); + TEST_BACK(TOY_CC_BACK_CYAN); + TEST_BACK(TOY_CC_BACK_WHITE); + TEST_BACK(TOY_CC_BACK_DEFAULT); +#undef TEST_BACK + } + + //test the commonly used shorthands + { +#define TEST_MACRO(value) printf(value #value TOY_CC_RESET "\n") + TEST_MACRO(TOY_CC_DEBUG); + TEST_MACRO(TOY_CC_NOTICE); + TEST_MACRO(TOY_CC_WARN); + TEST_MACRO(TOY_CC_ERROR); + TEST_MACRO(TOY_CC_ASSERT); + TEST_MACRO(TOY_CC_RESET); +#undef TEST_MACRO + } + + //This will always return zero, so it needs a visual check + return 0; +} + +int main(void) { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_callbacks(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_console_colors(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} + diff --git a/tests/units/test_scope.c b/tests/units/test_scope.c new file mode 100644 index 0000000..f79a451 --- /dev/null +++ b/tests/units/test_scope.c @@ -0,0 +1,9 @@ +#include "toy_scope.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +} diff --git a/tests/units/test_stack.c b/tests/units/test_stack.c new file mode 100644 index 0000000..ac0bee6 --- /dev/null +++ b/tests/units/test_stack.c @@ -0,0 +1,150 @@ +#include "toy_stack.h" +#include "toy_console_colors.h" + +#include + +int test_stack_basics(void) { + //allocate and free the stack + { + Toy_Stack* stack = Toy_allocateStack(); + + //check if it worked + if ( + stack == NULL || + stack->capacity != 8 || + stack->count != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to allocate Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + Toy_freeStack(stack); + } + + //push, peek and pop stack + { + Toy_Stack* stack = Toy_allocateStack(); + + //check if it worked (push) + Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(42)); + Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(69)); + Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(420)); + if ( + stack == NULL || + stack->capacity != 8 || + stack->count != 3) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to push Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + //check if it worked (peek) + Toy_Value top1 = Toy_peekStack(&stack); + if ( + TOY_VALUE_IS_INTEGER(top1) != true || + TOY_VALUE_AS_INTEGER(top1) != 420) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to peek Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + //check if it worked (pop) + Toy_Value top2 = Toy_popStack(&stack); + if ( + stack == NULL || + stack->capacity != 8 || + stack->count != 2 || + TOY_VALUE_IS_INTEGER(top2) != true || + TOY_VALUE_AS_INTEGER(top2) != 420) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + //check if it worked (post-pop peek) + Toy_Value top3 = Toy_peekStack(&stack); + if ( + TOY_VALUE_IS_INTEGER(top3) != true || + TOY_VALUE_AS_INTEGER(top3) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to pop then peek Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + Toy_freeStack(stack); + } + + return 0; +} + +int test_stack_stress(void) { + //stress the stack + { + Toy_Stack* stack = Toy_allocateStack(); + + //push 500 values + for (int i = 0; i < 500; i++) { + Toy_pushStack(&stack, TOY_VALUE_FROM_INTEGER(i)); + } + + //check if it worked + if ( + stack == NULL || + stack->capacity != 512 || + stack->count != 500) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to stress push the Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + //pop each value + while(stack->count > 0) { + Toy_popStack(&stack); //ignore the results + } + + //check if it worked + if ( + stack == NULL || + stack->capacity != TOY_STACK_INITIAL_CAPACITY || //reset to initial capacity + stack->count != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to stress pop the Toy_Stack\n" TOY_CC_RESET); + Toy_freeStack(stack); + return -1; + } + + //cleanup + Toy_freeStack(stack); + } + + return 0; +} + +int main(void) { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_stack_basics(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_stack_stress(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} diff --git a/tests/units/test_string.c b/tests/units/test_string.c new file mode 100644 index 0000000..5250dc4 --- /dev/null +++ b/tests/units/test_string.c @@ -0,0 +1,9 @@ +#include "toy_string.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +} diff --git a/tests/units/test_table.c b/tests/units/test_table.c new file mode 100644 index 0000000..ad1d753 --- /dev/null +++ b/tests/units/test_table.c @@ -0,0 +1,653 @@ +#include "toy_table.h" +#include "toy_console_colors.h" + +#include + +int test_table_allocation(void) { + //allocate and free a table + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //check + if (table == NULL) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate a table\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + return 0; +} + +int test_table_simple_insert_lookup_and_remove(void) { + //simple insert + { + //setup + Toy_Table* table = Toy_allocateTable(); + + Toy_Value key = TOY_VALUE_FROM_INTEGER(1); + Toy_Value value = TOY_VALUE_FROM_INTEGER(42); + + //insert + Toy_insertTable(&table, key, value); + if (table == NULL || + table->capacity != 8 || + table->count != 1) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to insert into a table\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //lookup + Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(1)); + + //check lookup + if (table == NULL || + table->capacity != 8 || + table->count != 1 || + TOY_VALUE_AS_INTEGER(result) != 42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to lookup from a table\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //remove + Toy_removeTable(&table, TOY_VALUE_FROM_INTEGER(1)); + + //check remove + if (table == NULL || + table->capacity != 8 || + table->count != 0) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to remove from a table\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + return 0; +} + +//macros are a godsend +#define TEST_ENTRY_STATE(i, k, v, p) \ + TOY_VALUE_IS_INTEGER(table->data[i].key) != true || \ + TOY_VALUE_AS_INTEGER(table->data[i].key) != k || \ + TOY_VALUE_IS_INTEGER(table->data[i].value) != true || \ + TOY_VALUE_AS_INTEGER(table->data[i].value) != v || \ + table->data[i].psl != p + +int test_table_contents_no_expansion(void) { + //single insert + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //insert a key and value + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 1 || + + TEST_ENTRY_STATE(7, 1, 42, 0) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, single insert {1:42}\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, no collisions + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(2), TOY_VALUE_FROM_INTEGER(69)); //hash: 0 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(420)); //hash: 5 + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 3 || + + TEST_ENTRY_STATE(7, 1, 42, 0) || + TEST_ENTRY_STATE(0, 2, 69, 0) || + TEST_ENTRY_STATE(5, 3, 420, 0) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, multiple inserts, no collisions\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, with collisions + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(5), TOY_VALUE_FROM_INTEGER(42)); //hash: 2 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(69)); //hash: 2 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(37), TOY_VALUE_FROM_INTEGER(420)); //hash: 2 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(65), TOY_VALUE_FROM_INTEGER(8891)); //hash: 2 + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 4 || + + TEST_ENTRY_STATE(2, 5, 42, 0) || + TEST_ENTRY_STATE(3, 19, 69, 1) || + TEST_ENTRY_STATE(4, 37, 420, 2) || + TEST_ENTRY_STATE(5, 65, 8891, 3) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, with collisions, modulo wrap + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(14), TOY_VALUE_FROM_INTEGER(69)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(76), TOY_VALUE_FROM_INTEGER(420)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(80), TOY_VALUE_FROM_INTEGER(8891)); //hash: 7 + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 4 || + + TEST_ENTRY_STATE(7, 1, 42, 0) || + TEST_ENTRY_STATE(0, 14, 69, 1) || + TEST_ENTRY_STATE(1, 76, 420, 2) || + TEST_ENTRY_STATE(2, 80, 8891, 3) + + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions, modulo wrap\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //lookup, with collisions, modulo wrap + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(69)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(420)); //hash: 7 + + //lookup + Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(33)); + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 3 || + + TOY_VALUE_IS_INTEGER(result) != true || + TOY_VALUE_AS_INTEGER(result) != 69 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad result from table lookup with collisions and modulo wrap\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, with collisions, modulo wrap, psl overlap + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(69)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(420)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(8891)); //hash: 1 + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 4 || + + TEST_ENTRY_STATE(7, 17, 42, 0) || + TEST_ENTRY_STATE(0, 33, 69, 1) || + TEST_ENTRY_STATE(1, 70, 420, 2) || + TEST_ENTRY_STATE(2, 35, 8891, 1) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions, modulo wrap, psl overlap\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, with collisions, modulo wrap, psl overlap, psl shift + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(69)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(420)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(8891)); //hash: 1 + + //remove + Toy_removeTable(&table, TOY_VALUE_FROM_INTEGER(33)); + + //check the state + if (table == NULL || + table->capacity != 8 || + table->count != 3 || + + TEST_ENTRY_STATE(7, 17, 42, 0) || + TEST_ENTRY_STATE(0, 70, 420, 1) || + TEST_ENTRY_STATE(1, 35, 8891, 0) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data, muiltiple inserts, with collisions, modulo wrap, psl overlap, psl shift\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + return 0; +} + +int test_table_contents_with_expansions(void) { + //simple expansion + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //insert a key and value + for (int i = 0; i < 20; i++) { + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(i), TOY_VALUE_FROM_INTEGER(42)); + } + + //check the state + if (table == NULL || + table->capacity != 32 || + table->count != 20 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to expand table capacity\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //expansion, multiple inserts, no collisions + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(0), TOY_VALUE_FROM_INTEGER(42)); //hash: 0 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(42)); //hash: 1 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(42)); //hash: 2 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(8), TOY_VALUE_FROM_INTEGER(42)); //hash: 3 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(10), TOY_VALUE_FROM_INTEGER(42)); //hash: 4 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(42)); //hash: 5 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(28), TOY_VALUE_FROM_INTEGER(42)); //hash: 6 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(6), TOY_VALUE_FROM_INTEGER(42)); //hash: 8 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(93), TOY_VALUE_FROM_INTEGER(42)); //hash: 9 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(85), TOY_VALUE_FROM_INTEGER(42)); //hash: 10 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(9), TOY_VALUE_FROM_INTEGER(42)); //hash: 11 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(11), TOY_VALUE_FROM_INTEGER(42)); //hash: 12 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(22), TOY_VALUE_FROM_INTEGER(42)); //hash: 13 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(13), TOY_VALUE_FROM_INTEGER(42)); //hash: 14 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 15 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(43), TOY_VALUE_FROM_INTEGER(42)); //hash: 16 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(4), TOY_VALUE_FROM_INTEGER(42)); //hash: 17 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(5), TOY_VALUE_FROM_INTEGER(42)); //hash: 18 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(7), TOY_VALUE_FROM_INTEGER(42)); //hash: 19 + + //check the state + if (table == NULL || + table->capacity != 32 || + table->count != 20 || + + //effectively, check that each key was placed in the correct position after the expansion + TEST_ENTRY_STATE(0, 0, 42, 0) || + TEST_ENTRY_STATE(1, 35, 42, 0) || + TEST_ENTRY_STATE(2, 19, 42, 0) || + TEST_ENTRY_STATE(3, 8, 42, 0) || + TEST_ENTRY_STATE(4, 10, 42, 0) || + TEST_ENTRY_STATE(5, 3, 42, 0) || + TEST_ENTRY_STATE(6, 28, 42, 0) || + TEST_ENTRY_STATE(7, 1, 42, 0) || + TEST_ENTRY_STATE(8, 6, 42, 0) || + TEST_ENTRY_STATE(9, 93, 42, 0) || + TEST_ENTRY_STATE(10, 85, 42, 0) || + TEST_ENTRY_STATE(11, 9, 42, 0) || + TEST_ENTRY_STATE(12, 11, 42, 0) || + TEST_ENTRY_STATE(13, 22, 42, 0) || + TEST_ENTRY_STATE(14, 13, 42, 0) || + TEST_ENTRY_STATE(15, 17, 42, 0) || + TEST_ENTRY_STATE(16, 43, 42, 0) || + TEST_ENTRY_STATE(17, 4, 42, 0) || + TEST_ENTRY_STATE(18, 5, 42, 0) || + TEST_ENTRY_STATE(19, 7, 42, 0) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data after expansion, multiple inserts, no collisions\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, with collisions + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(0), TOY_VALUE_FROM_INTEGER(42)); //hash: 0 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(42)); //hash: 1 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(42)); //hash: 2 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(8), TOY_VALUE_FROM_INTEGER(42)); //hash: 3 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(10), TOY_VALUE_FROM_INTEGER(42)); //hash: 4 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(42)); //hash: 5 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(28), TOY_VALUE_FROM_INTEGER(42)); //hash: 6 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 7 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(6), TOY_VALUE_FROM_INTEGER(42)); //hash: 8 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(93), TOY_VALUE_FROM_INTEGER(42)); //hash: 9 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(85), TOY_VALUE_FROM_INTEGER(42)); //hash: 10 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(9), TOY_VALUE_FROM_INTEGER(42)); //hash: 11 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(11), TOY_VALUE_FROM_INTEGER(42)); //hash: 12 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(22), TOY_VALUE_FROM_INTEGER(42)); //hash: 13 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(13), TOY_VALUE_FROM_INTEGER(42)); //hash: 14 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(17), TOY_VALUE_FROM_INTEGER(42)); //hash: 15 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(43), TOY_VALUE_FROM_INTEGER(42)); //hash: 16 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(4), TOY_VALUE_FROM_INTEGER(42)); //hash: 17 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(5), TOY_VALUE_FROM_INTEGER(42)); //hash: 18 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(7), TOY_VALUE_FROM_INTEGER(42)); //hash: 19 + + //insert one more + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(70), TOY_VALUE_FROM_INTEGER(42)); //hash: 15 + + //check the state + if (table == NULL || + table->capacity != 32 || + table->count != 21 || + + TEST_ENTRY_STATE(0, 0, 42, 0) || + TEST_ENTRY_STATE(1, 35, 42, 0) || + TEST_ENTRY_STATE(2, 19, 42, 0) || + TEST_ENTRY_STATE(3, 8, 42, 0) || + TEST_ENTRY_STATE(4, 10, 42, 0) || + TEST_ENTRY_STATE(5, 3, 42, 0) || + TEST_ENTRY_STATE(6, 28, 42, 0) || + TEST_ENTRY_STATE(7, 1, 42, 0) || + TEST_ENTRY_STATE(8, 6, 42, 0) || + TEST_ENTRY_STATE(9, 93, 42, 0) || + TEST_ENTRY_STATE(10, 85, 42, 0) || + TEST_ENTRY_STATE(11, 9, 42, 0) || + TEST_ENTRY_STATE(12, 11, 42, 0) || + TEST_ENTRY_STATE(13, 22, 42, 0) || + TEST_ENTRY_STATE(14, 13, 42, 0) || + TEST_ENTRY_STATE(15, 17, 42, 0) || + + TEST_ENTRY_STATE(16, 70, 42, 1) || //the collision + + TEST_ENTRY_STATE(17, 43, 42, 1) || + TEST_ENTRY_STATE(18, 4, 42, 1) || + TEST_ENTRY_STATE(19, 5, 42, 1) || + TEST_ENTRY_STATE(20, 7, 42, 1) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data after expansion, muiltiple inserts, with collisions\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //multiple inserts, with collisions, modulo wrap + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(123), TOY_VALUE_FROM_INTEGER(42)); //hash: 20 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(42), TOY_VALUE_FROM_INTEGER(42)); //hash: 21 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(132), TOY_VALUE_FROM_INTEGER(42)); //hash: 22 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(34), TOY_VALUE_FROM_INTEGER(42)); //hash: 23 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(2), TOY_VALUE_FROM_INTEGER(42)); //hash: 24 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(32), TOY_VALUE_FROM_INTEGER(42)); //hash: 25 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(21), TOY_VALUE_FROM_INTEGER(42)); //hash: 26 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(44), TOY_VALUE_FROM_INTEGER(42)); //hash: 27 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(104), TOY_VALUE_FROM_INTEGER(42)); //hash: 28 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(15), TOY_VALUE_FROM_INTEGER(42)); //hash: 29 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(57), TOY_VALUE_FROM_INTEGER(42)); //hash: 30 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(33), TOY_VALUE_FROM_INTEGER(42)); //hash: 31 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(0), TOY_VALUE_FROM_INTEGER(42)); //hash: 32 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(35), TOY_VALUE_FROM_INTEGER(42)); //hash: 33 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(19), TOY_VALUE_FROM_INTEGER(42)); //hash: 34 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(8), TOY_VALUE_FROM_INTEGER(42)); //hash: 35 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(10), TOY_VALUE_FROM_INTEGER(42)); //hash: 36 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(3), TOY_VALUE_FROM_INTEGER(42)); //hash: 37 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(28), TOY_VALUE_FROM_INTEGER(42)); //hash: 38 + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(1), TOY_VALUE_FROM_INTEGER(42)); //hash: 39 + + //insert one more + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(79), TOY_VALUE_FROM_INTEGER(42)); //hash: 23 + + //check the state + if (table == NULL || + table->capacity != 32 || + table->count != 21 || + + TEST_ENTRY_STATE(20, 123, 42, 0) || + TEST_ENTRY_STATE(21, 42, 42, 0) || + TEST_ENTRY_STATE(22, 132, 42, 0) || + TEST_ENTRY_STATE(23, 34, 42, 0) || + + TEST_ENTRY_STATE(24, 79, 42, 1) || //the collision + + TEST_ENTRY_STATE(25, 2, 42, 1) || + TEST_ENTRY_STATE(26, 32, 42, 1) || + TEST_ENTRY_STATE(27, 21, 42, 1) || + TEST_ENTRY_STATE(28, 44, 42, 1) || + TEST_ENTRY_STATE(29, 104, 42, 1) || + TEST_ENTRY_STATE(30, 15, 42, 1) || + TEST_ENTRY_STATE(31, 57, 42, 1) || + TEST_ENTRY_STATE(0, 33, 42, 1) || + TEST_ENTRY_STATE(1, 0, 42, 1) || + TEST_ENTRY_STATE(2, 35, 42, 1) || + TEST_ENTRY_STATE(3, 19, 42, 1) || + TEST_ENTRY_STATE(4, 8, 42, 1) || + TEST_ENTRY_STATE(5, 10, 42, 1) || + TEST_ENTRY_STATE(6, 3, 42, 1) || + TEST_ENTRY_STATE(7, 28, 42, 1) || + TEST_ENTRY_STATE(8, 1, 42, 1) + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unrecognized state from table data after expansion, muiltiple inserts, with collisions, modulo wrap\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //lookup, with collisions, modulo wrap + { + //setup + Toy_Table* table = Toy_allocateTable(); + + //inserts + for (int i = 0; i < 20; i++) { //enough to expand + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(i), TOY_VALUE_FROM_INTEGER(100 - i)); + } + + //lookup + Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(15)); + + //check the state + if (table == NULL || + table->capacity != 32 || + table->count != 20 || + + TOY_VALUE_IS_INTEGER(result) != true || + TOY_VALUE_AS_INTEGER(result) != 85 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Bad result from table lookup after expansion, with collisions and modulo wrap\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + //Skipped: multiple inserts, with collisions, modulo wrap, psl overlap + //Skipped: multiple inserts, with collisions, modulo wrap, psl overlap, psl shift + + //Note: since psl overlap and psl shift both work without expansion, I'm leaving these tests unimplemented. + + return 0; +} + +int test_table_expansions_under_stress(void) { + //multiple expansions, find one value + { + //setup + Toy_Table* table = Toy_allocateTable(); + + int top = 300; + + //insert keys and values + for (int i = 0; i < 400; i++) { + Toy_insertTable(&table, TOY_VALUE_FROM_INTEGER(i), TOY_VALUE_FROM_INTEGER(top - i)); + } + + Toy_Value result = Toy_lookupTable(&table, TOY_VALUE_FROM_INTEGER(265)); + + //check the state + if (table == NULL || + table->capacity != 512 || + table->count != 400 || + + TOY_VALUE_IS_INTEGER(result) != true || + TOY_VALUE_AS_INTEGER(result) != 35 + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Table expansions under stress failed\n" TOY_CC_RESET); + Toy_freeTable(table); + return -1; + } + + //free + Toy_freeTable(table); + } + + return 0; +} + +int main(void) { + //run each test set, returning the total errors given + int total = 0, res = 0; + + { + res = test_table_allocation(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_table_simple_insert_lookup_and_remove(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_table_contents_no_expansion(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_table_contents_with_expansions(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + { + res = test_table_expansions_under_stress(); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + return total; +} diff --git a/tests/units/test_value.c b/tests/units/test_value.c new file mode 100644 index 0000000..f08245f --- /dev/null +++ b/tests/units/test_value.c @@ -0,0 +1,9 @@ +#include "toy_value.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +} diff --git a/tests/units/test_vm.c b/tests/units/test_vm.c new file mode 100644 index 0000000..a83d5bf --- /dev/null +++ b/tests/units/test_vm.c @@ -0,0 +1,9 @@ +#include "toy_vm.h" +#include "toy_console_colors.h" + +#include + +int main(void) { + printf(TOY_CC_WARN "Test not yet implemented: %s\n" TOY_CC_RESET, __FILE__); + return -1; +}