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; +}