From 138792df48f62801da14acd259a5a5a0842d9373 Mon Sep 17 00:00:00 2001 From: Rickey Fehr Date: Fri, 17 Oct 2025 18:01:34 -0700 Subject: [PATCH] Add memory pool test, fix bugs --- .github/workflows/build_ci_workflow.yml | 3 + .gitignore | 1 + Makefile | 18 ++--- include/pool.h | 18 ++--- scripts/get_memory_map.sh | 1 - source/pool.c | 14 ++-- tests/pool/Makefile | 17 +++++ tests/pool/pool_test.c | 87 +++++++++++++++++++++++++ tests/pool/test_pools.def | 6 ++ tests/pool/test_structures.h | 18 +++++ tests/run_tests.sh | 13 ++++ 11 files changed, 171 insertions(+), 25 deletions(-) create mode 100644 tests/pool/Makefile create mode 100644 tests/pool/pool_test.c create mode 100644 tests/pool/test_pools.def create mode 100644 tests/pool/test_structures.h create mode 100755 tests/run_tests.sh diff --git a/.github/workflows/build_ci_workflow.yml b/.github/workflows/build_ci_workflow.yml index ef25e1a..72b7cd3 100644 --- a/.github/workflows/build_ci_workflow.yml +++ b/.github/workflows/build_ci_workflow.yml @@ -53,6 +53,9 @@ jobs: echo "UID=$(id -u)" >> "$GITHUB_ENV" echo "GID=$(id -g)" >> "$GITHUB_ENV" + - name: Run Tests + run: cd tests && ./run_tests.sh + - name: Build the project run: docker compose -f docker-compose.yml run --rm balatro sh -c "make -j$(nproc) && ./scripts/get_memory_map.sh" diff --git a/.gitignore b/.gitignore index be9866f..38bce4e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.sln *.vcxproj* /balatro-gba/x64/** +/tests/pool/build diff --git a/Makefile b/Makefile index d187d39..9e4b0fe 100644 --- a/Makefile +++ b/Makefile @@ -25,13 +25,14 @@ LIBTONC := $(DEVKITPRO)/libtonc # the makefile is found # #--------------------------------------------------------------------------------- -TARGET := $(notdir $(CURDIR)) -BUILD := build -SOURCES := source -INCLUDES := include -DATA := -MUSIC := audio -GRAPHICS := graphics +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +INCLUDES := include +DATA := +MUSIC := audio +GRAPHICS := graphics +POOLS_DEF_FILE := \"pools.def\" #--------------------------------------------------------------------------------- # options for code generation @@ -42,7 +43,8 @@ GIT_DIRTY := $(shell git diff-index --quiet HEAD -- || echo "-dirty") GIT_HASH := $(shell git rev-parse --short HEAD || echo "undef") GIT_C_FLAGS := -DGIT_HASH=\"$(GIT_HASH)\" -DGIT_DIRTY=\"$(GIT_DIRTY)\" -CFLAGS := -g -O3 -Wall -Werror\ +CFLAGS := -g -O3 -Wall -Werror \ + -DPOOLS_DEF_FILE=$(POOLS_DEF_FILE) \ -mcpu=arm7tdmi -mtune=arm7tdmi \ -ffast-math -fomit-frame-pointer -funroll-loops \ $(ARCH) diff --git a/include/pool.h b/include/pool.h index ebdba1b..a3d2e13 100644 --- a/include/pool.h +++ b/include/pool.h @@ -1,16 +1,16 @@ #ifndef POOL_H #define POOL_H -#include +#include #define POOL_BITS_PER_WORD 32 -#define POOL_MAX_CAPACITY 128 +#define POOL_BITMAP_BYTES 8 typedef struct PoolBitmap { - u32 *w; - u32 nbits; - u32 nwords; - u32 cap; + uint32_t *w; + uint32_t nbits; + uint32_t nwords; + uint32_t cap; } PoolBitmap; void pool_bm_clear_idx(PoolBitmap *bm, int idx); @@ -27,13 +27,13 @@ int pool_bm_get_free_idx(PoolBitmap *bm); #define POOL_DEFINE_TYPE(type, capacity) \ static type type##_storage[capacity]; \ - static u32 type##_bitmap_w[sizeof(u32)] = {0}; \ + static uint32_t type##_bitmap_w[POOL_BITMAP_BYTES] = {0}; \ static type##Pool type##_pool = \ { \ .bm = { \ .w = type##_bitmap_w, \ .nbits = POOL_BITS_PER_WORD, \ - .nwords = sizeof(u32), \ + .nwords = POOL_BITMAP_BYTES, \ .cap = capacity, \ }, \ .objects = type##_storage, \ @@ -56,7 +56,7 @@ int pool_bm_get_free_idx(PoolBitmap *bm); #define POOL_ENTRY(name, capacity) \ POOL_DECLARE_TYPE(name); -#include "pools.def" +#include POOLS_DEF_FILE #undef POOL_ENTRY #endif // POOL_H diff --git a/scripts/get_memory_map.sh b/scripts/get_memory_map.sh index ed311c1..035c952 100755 --- a/scripts/get_memory_map.sh +++ b/scripts/get_memory_map.sh @@ -61,7 +61,6 @@ for name in $(get_pool_names); do pool_size="$(cut -d ' ' -f 3 <<< $output_pool)" func_size="$(cut -d ' ' -f 3 <<< $output_func)" bm_size="$(cut -d ' ' -f 3 <<< $output_bm)" - #bm_size=16 #always gonna be 16, 4 * sizeof(u32) TOTAL_BYTES=$(( TOTAL_BYTES + pool_size + func_size + bm_size )) diff --git a/source/pool.c b/source/pool.c index 29ae12c..0b526a6 100644 --- a/source/pool.c +++ b/source/pool.c @@ -3,17 +3,17 @@ void pool_bm_clear_idx(PoolBitmap *bm, int idx) { // Divide by 32 to get the word index - u32 i = idx >> 5; + uint32_t i = idx >> 5; // Get last 5-bits, same as a modulo (% 32) operation on positive numbers - u32 b = idx & 0x1F; - bm->w[i] &= ~((u32)1 << b); + uint32_t b = idx & 0x1F; + bm->w[i] &= ~((uint32_t)1 << b); } int pool_bm_get_free_idx(PoolBitmap *bm) { - for (u32 i = 0; i < bm->nwords; i++) + for (uint32_t i = 0; i < bm->nwords; i++) { - u32 inv = ~bm->w[i]; + uint32_t inv = ~bm->w[i]; // guard so we don't call `ctz` with 0, since __builtin_ctz(0) is undefined // https://gcc.gnu.org/onlinedocs/gcc/Bit-Operation-Builtins.html#index-_005f_005fbuiltin_005fctz @@ -26,7 +26,7 @@ int pool_bm_get_free_idx(PoolBitmap *bm) if (inv) { int bit = __builtin_ctz(inv); - bm->w[i] |= ((u32)1 << bit); + bm->w[i] |= ((uint32_t)1 << bit); int idx = i * POOL_BITS_PER_WORD + bit; return (idx < bm->cap) ? idx : -1; } @@ -38,5 +38,5 @@ int pool_bm_get_free_idx(PoolBitmap *bm) #define POOL_ENTRY(name, capacity) \ POOL_DEFINE_TYPE(name, capacity); -#include "pools.def" +#include POOLS_DEF_FILE #undef POOL_ENTRY diff --git a/tests/pool/Makefile b/tests/pool/Makefile new file mode 100644 index 0000000..738332e --- /dev/null +++ b/tests/pool/Makefile @@ -0,0 +1,17 @@ +POOLS_DEF_FILE := \"test_pools.def\" + +CC := gcc +CFLAGS := -I../../include -I. \ + -g -O3 -Wall -Werror -DPOOLS_DEF_FILE=$(POOLS_DEF_FILE) + +SRC := pool_test.c ../../source/pool.c +OUT := build/pool_test + +$(OUT): $(SRC) | build + $(CC) $(CFLAGS) -o $@ $^ + +build: + mkdir -p build + +clean: + rm -f $(OUT) diff --git a/tests/pool/pool_test.c b/tests/pool/pool_test.c new file mode 100644 index 0000000..838c01e --- /dev/null +++ b/tests/pool/pool_test.c @@ -0,0 +1,87 @@ +#include "pool.h" +#include "test_structures.h" + +#include +#include +#include +#include + +int get_random(int low, int high) +{ + srand(time(NULL)); + int n = low + rand() % (high - low + 1); + return n; +} + +bool test_fill(ChunkOfData* myPtrs[], int check_size) +{ + int itr = 0; + + ChunkOfData* test_chunk = NULL; + do + { + test_chunk = POOL_GET(ChunkOfData); + if(test_chunk != NULL) myPtrs[itr++] = test_chunk; + } while(test_chunk != NULL); + + if(itr != check_size) + { + fprintf(stderr, "Error: failed to get expected number of valid pointers\n" + " expected: %d, actual %d\n", check_size, itr); + return false; + } + + return true; +} + +bool test_fill_and_empty(void) +{ + ChunkOfData* myPtrs[TEST_SIZE]; + if(!test_fill(myPtrs, TEST_SIZE)) return false; + + for(int itr = TEST_SIZE; itr >= 0; --itr) + { + POOL_FREE(ChunkOfData, myPtrs[itr]); + } + + return true; +} + +bool test_fill_and_remove_at_random_and_refill_and_empty(void) +{ + ChunkOfData* myPtrs[TEST_SIZE]; + if(!test_fill(myPtrs, TEST_SIZE)) return false; + + // remove between 10 and TEST_SIZE + int number_to_remove = get_random(100, TEST_SIZE); + + for(int itr = 0; itr < number_to_remove; itr++) + { + POOL_FREE(ChunkOfData, myPtrs[itr]); + } + + if(!test_fill(myPtrs, number_to_remove)) return false; + + for(int itr = 0; itr < TEST_SIZE; itr++) + { + POOL_FREE(ChunkOfData, myPtrs[itr]); + } + + return true; +} + +int main(void) +{ + // Test it twice to make sure empty works, kinda hacky. + if(!test_fill_and_empty()) return -1; + if(!test_fill_and_empty()) return -1; + + // Similarly here, verify that fill, random num removal, refill, and empty + // by refilling and emptying again + if(!test_fill_and_remove_at_random_and_refill_and_empty()) return -1; + if(!test_fill_and_empty()) return -1; + + printf("Pool Tests Passed\n"); + + return 0; +} diff --git a/tests/pool/test_pools.def b/tests/pool/test_pools.def new file mode 100644 index 0000000..a5373c8 --- /dev/null +++ b/tests/pool/test_pools.def @@ -0,0 +1,6 @@ +#include "test_structures.h" + +#define TEST_SIZE 240 + +POOL_ENTRY(ChunkOfData, TEST_SIZE); + diff --git a/tests/pool/test_structures.h b/tests/pool/test_structures.h new file mode 100644 index 0000000..1f72c86 --- /dev/null +++ b/tests/pool/test_structures.h @@ -0,0 +1,18 @@ +#ifndef POOL_TEST_STRUCTURES +#define POOL_TEST_STRUCTURES + +#include + +typedef struct +{ + union + { + int my_int; + float my_float; + char my_char; + } GenericData; + + int my_type; +} ChunkOfData; + +#endif // POOL_TEST_STRUCTURES diff --git a/tests/run_tests.sh b/tests/run_tests.sh new file mode 100755 index 0000000..86b69f7 --- /dev/null +++ b/tests/run_tests.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -eu + +run_pool_test() { + cd pool + make clean + make + ./build/pool_test + cd - > /dev/null +} + +run_pool_test