diff --git a/.DS_Store b/.DS_Store index 96ac74d..1fe44ad 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..5b159aa --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,8 @@ +Checks: > + -*, + clang-analyzer-*, + bugprone-*, + -bugprone-easily-swappable-parameters, + -bugprone-reserved-identifier, + performance-*, + security-* diff --git a/.gitignore b/.gitignore index 5b79a54..640f1a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ build/ -.cache/ \ No newline at end of file +.cache/ +tests/cache/ +tests/bin/ +coverage.html diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 14d3d7c..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,37 +0,0 @@ -# AGENTS.md - -## Identity & Core Philosophy -You are an expert bug-hunter agent. -Speak and reason in **Caveman style** (why use many token when few token do trick). - -### Caveman Communication Rules -- Drop articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries, and greetings. -- Short, dense, direct phrases. Fragment sentences okay. -- Never summarize files read. Report only findings. -- **No tool-call narration:** Fire tools directly. Never explain what you are about to inspect or have just inspected. -- **Tokenizer rules:** NEVER invent abbreviations (cfg/impl/req/res/fn) or use arrows (→); tokenizers split them. Keep full words. -- **Safety rules:** NEVER drop `not`, `never`, `no`, `only`, `except` (flips meaning). -- **Exceptions (Do NOT caveman):** Code snippets, file paths, line numbers, command names, and error traces remain exact and intact. -- **Output pattern:** `[thing] [action] [reason]. [next step].` - ---- - -## Strict Permissions & Boundaries -1. **Target Task:** Read codebase, hunt bugs, identify security flaws, edge-case failures, or performance bottlenecks. -2. **File Mutation Constraint:** - - **YOU MAY ONLY CREATE OR EDIT:** `BUGS.md` - - **ABSOLUTE BAN:** You are strictly forbidden from modifying, patching, refactoring, deleting, or creating ANY other files in the workspace (no source files, configs, tests, or documentation). - - If user asks you to fix code directly, REFUSE in caveman: "No touch code. Rule say only log to BUGS.md." - ---- - -## Operating Instructions -1. Inspect files in read-only mode using available search/read tools. Call them immediately without introductory text. -2. Read existing `BUGS.md` first: - - Identify existing style, headers, keys, table columns, or list formats. - - You MUST mirror existing format, structure, and naming conventions exactly. - - If `BUGS.md` is empty or missing, use a minimal markdown list: `File:Line | Severity | Issue | Recommended fix`. -3. For every confirmed issue found: - - Append or edit entry in `BUGS.md` following matched format. - - Keep bug descriptions in `BUGS.md` terse and caveman-style. -4. Do not patch or edit source code under any circumstances. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e004316 --- /dev/null +++ b/Makefile @@ -0,0 +1,129 @@ +# ============================================================================== +# Toolchain & Executables +# ============================================================================== +LLVM_DIR ?= /opt/homebrew/opt/llvm/bin +CC := $(LLVM_DIR)/clang +LLVM_PROFDATA := $(LLVM_DIR)/llvm-profdata +LLVM_COV := $(LLVM_DIR)/llvm-cov +CPPCHECK := cppcheck + +# ============================================================================== +# Flags & Directories +# ============================================================================== +CFLAGS := -std=c23 -Wall -Wextra -Wpedantic -Wshadow \ + -Wconversion -Wsign-conversion \ + -g -O1 -fno-omit-frame-pointer +SAN_FLAGS := -fsanitize=address,undefined +INCLUDES := -Iinclude -Isrc/lib -Isrc/lib/char -Isrc/lib/utils + +ASAN_ENV := ASAN_OPTIONS="allocator_may_return_null=1:detect_stack_use_after_return=1" \ + UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=1" + +CPPCHECK_FLAGS := --enable=all \ + --inconclusive \ + --error-exitcode=1 \ + --check-level=exhaustive \ + --suppress=missingIncludeSystem \ + --suppress=unmatchedSuppression \ + --suppress=unusedFunction \ + --suppress=staticFunction \ + --inline-suppr \ + $(INCLUDES) + +# Sources & Build Directories +SRCS := src/lib/char/char.c src/lib/utils/utils.c +TEST_SRC := tests/test_learn.c +FUZZ_SRC := tests/fuzz_char.c + +BIN_DIR := tests/bin +CACHE_DIR := tests/cache + +TEST_BIN := $(BIN_DIR)/test_learn_runner +TSAN_BIN := $(BIN_DIR)/test_tsan_runner +FUZZ_BIN := $(BIN_DIR)/fuzz_char_runner +COV_BIN := $(BIN_DIR)/test_cov_runner + +# ============================================================================== +# Targets & Workflow Rules +# ============================================================================== +.PHONY: all test sanitize check check-sources analyze cppcheck fuzz cov-build cov clean + +all: test + +$(BIN_DIR) $(CACHE_DIR): + @mkdir -p $@ + +# --- Compilation Targets --- + +$(TEST_BIN): $(SRCS) $(TEST_SRC) | $(BIN_DIR) + $(CC) $(CFLAGS) $(SAN_FLAGS) $(INCLUDES) $^ -o $@ + +$(TSAN_BIN): $(SRCS) $(TEST_SRC) | $(BIN_DIR) + $(CC) $(CFLAGS) -fsanitize=thread $(INCLUDES) $^ -o $@ + +$(FUZZ_BIN): $(SRCS) $(FUZZ_SRC) | $(BIN_DIR) + $(CC) $(CFLAGS) -fsanitize=fuzzer,address,undefined $(INCLUDES) $^ -o $@ + +# --- Testing & Sanitizer Suites --- + +test: $(TEST_BIN) + @echo "=== Running Unit Tests (ASan + UBSan) ===" + @$(ASAN_ENV) ./$(TEST_BIN) + +sanitize: $(TEST_BIN) $(TSAN_BIN) + @echo "\n=== [1/2] Running Address + UndefinedBehavior Sanitizers ===" + @$(ASAN_ENV) ./$(TEST_BIN) + @echo "\n=== [2/2] Running ThreadSanitizer ===" + @./$(TSAN_BIN) + @echo "\n>>> Sanitizers passed with zero defects." + +# Unified master check: runtime sanitizers, static analyzer, and cppcheck +check: sanitize analyze cppcheck + @echo "\n==============================================" + @echo " ALL TESTS, SANITIZERS & STATIC CHECKS PASSED " + @echo "==============================================" + +fuzz: $(FUZZ_BIN) + @echo "=== Starting Fuzzer (Press Ctrl+C to stop) ===" + @$(ASAN_ENV) ./$(FUZZ_BIN) -max_total_time=60 + +# --- Coverage Profiling --- + +cov-build: $(SRCS) $(TEST_SRC) | $(BIN_DIR) + $(CC) $(CFLAGS) $(SAN_FLAGS) -fprofile-instr-generate -fcoverage-mapping $(INCLUDES) $^ -o $(COV_BIN) + +cov: cov-build | $(CACHE_DIR) + @echo "=== Recording Coverage Profile Data ===" + @$(ASAN_ENV) LLVM_PROFILE_FILE="$(CACHE_DIR)/test_char.profdata_raw" ./$(COV_BIN) + @$(LLVM_PROFDATA) merge -sparse $(CACHE_DIR)/test_char.profdata_raw -o $(CACHE_DIR)/test_char.profdata + @echo "\n=== Line & Branch Coverage Report: src/lib/char/char.c ===" + @$(LLVM_COV) report $(COV_BIN) -instr-profile=$(CACHE_DIR)/test_char.profdata src/lib/char/char.c + @$(LLVM_COV) show $(COV_BIN) -instr-profile=$(CACHE_DIR)/test_char.profdata src/lib/char/char.c --format=html > coverage.html + @echo "\nDetailed HTML report written to coverage.html." + +# --- Static Analysis & Object Checking --- + +check-sources: | $(BIN_DIR) + @echo "=== Compiling Sources to Object Files (ASan + UBSan) ===" + $(CC) $(CFLAGS) $(SAN_FLAGS) $(INCLUDES) -c src/lib/char/char.c -o $(BIN_DIR)/char.o + $(CC) $(CFLAGS) $(SAN_FLAGS) $(INCLUDES) -c src/lib/utils/utils.c -o $(BIN_DIR)/utils.o + @echo "Library sources compiled cleanly." + +analyze: + @echo "=== Running Clang Static Analyzer ===" + @$(CC) $(CFLAGS) $(INCLUDES) \ + --analyze \ + -Xanalyzer -analyzer-output=text \ + -Xanalyzer -analyzer-checker=core,unix,deadcode,security \ + $(SRCS) + @echo "Clang Static Analyzer complete: zero defects." + +cppcheck: + @echo "=== Running Cppcheck Static Analysis ===" + @$(CPPCHECK) $(CPPCHECK_FLAGS) include/ src/ + @echo "Cppcheck passed with zero defects." + +# --- Housekeeping --- + +clean: + rm -rf $(BIN_DIR)/* $(CACHE_DIR)/* coverage.html diff --git a/Notes.md b/Notes.md deleted file mode 100644 index a1943b0..0000000 --- a/Notes.md +++ /dev/null @@ -1,23 +0,0 @@ -```mermaid -flowchart TD - Start([Function Called]) --> CheckErr{Did an error occur?} - - %% Error path - CheckErr -->|Yes| PushBit["Bitwise OR error code into err
err |= ERROR_BIT"] - PushBit --> CheckOrigin{Is origin == NULL?} - CheckOrigin -->|Yes| SetOrigin["Record location in origin
origin = __func__ / __FILE__"] - CheckOrigin -->|No| KeepOrigin[Preserve original root cause] - - SetOrigin --> ReturnStruct - KeepOrigin --> ReturnStruct - - %% Success / Normal path - CheckErr -->|No| ComputeValue["Write computation result
value = result"] - ComputeValue --> ReturnStruct - - ReturnStruct(["Return / Pass Out _err_t"]) --> Caller{Caller Inspection} - - Caller -->|Check errors| InspectBits[Inspect bit flags in err] - Caller -->|Read payload| ReadValue[Access value data] - Caller -->|Debug / Log| PrintOrigin[Print root cause origin] -``` diff --git a/src/.DS_Store b/src/.DS_Store index 25b538d..f0a8e65 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/lib/.DS_Store b/src/lib/.DS_Store index bcc091a..561adb0 100644 Binary files a/src/lib/.DS_Store and b/src/lib/.DS_Store differ diff --git a/src/lib/char/char.c b/src/lib/char/char.c index 7088afb..4a54b44 100644 --- a/src/lib/char/char.c +++ b/src/lib/char/char.c @@ -7,42 +7,11 @@ #include "utils/utils.h" -/** - * @file char.c - * @brief Dynamic contiguous array specialization for raw char elements. - * - * DESIGN CONTRACT: - * - RAW BYTE CONTAINER: - * This vector manages a sequence of independent 'char' values (raw bytes). - * It is NOT a C-string container. Buffers are NEVER null-terminated ('\0'). - * Passing _vec_char_data() directly to string utilities (e.g., strlen, printf - * %s) or treating it as a NUL-terminated string is undefined behavior. - * - * - ELEMENT VALIDITY & BOUNDS: - * Valid elements reside strictly in the half-open range [0, _internal_size). - * Slots in the range [_internal_size, _internal_capacity) are spare capacity - * and must be treated as uninitialized storage. Any access past - * _internal_size - 1 is out of bounds. - * - * - ERROR HANDLING: - * Functions taking an `err_t*` out-parameter require a non-NULL pointer. - * On error, appropriate bit flags (defined in utils.h) are bitwise OR-ed into - * `err->flags`, and the initial failure site is recorded in `err->origin`. - * - * - MEMORY OWNERSHIP: - * Buffers are heap-allocated via malloc/realloc and owned exclusively by - * the vector instance. Use _vec_char_delete() to release resources. - */ - __attribute__((overloadable)) size_t __null_check(const _VecCharInternal* ptr, err_t* status) { if (status == nullptr) { - err_t err = ERR_T_INIT; - err.flags = ERR_ERR_T_NULL; - err.origin = "NULL_CHECK"; - *status = err; return ERR_ERR_T_NULL; } if (ptr == nullptr) @@ -61,6 +30,10 @@ __null_check(const _VecCharInternal* ptr, err_t* status) __attribute__((overloadable)) size_t __null_check(const char* ptr, err_t* status) { + if (status == nullptr) + { + return ERR_ERR_T_NULL; + } if (ptr == nullptr) { status->flags |= ERR_BUF_NULL; @@ -71,6 +44,10 @@ __null_check(const char* ptr, err_t* status) size_t __origin_override(err_t* status, char* str) { + if (status == nullptr) + { + return ERR_ERR_T_NULL; + } if (status->origin != nullptr) { status->flags |= ERR_ORIGIN_UNALTERABLE; @@ -108,8 +85,13 @@ _vec_char_cpy(const char* src, const size_t src_size, err_t* err) _VecCharInternal self = VEC_INIT; err_t status = ERR_T_INIT; - if (__null_check(src, &status) != 0) + size_t null = __null_check(src, err); + if (null != 0) { + if (null & ERR_ERR_T_NULL) + { + return self; + } __origin_override(err, "CPY"); err->flags |= status.flags; return self; @@ -166,7 +148,7 @@ _vec_char_push_back(_VecCharInternal* ptr, const char val, err_t* err) if (res == nullptr) { __origin_override(err, "PUSH_BACK"); - err->flags |= ERR_BUF_NULL; + err->flags |= ERR_ALLOC_FAIL; return; } ptr->_internal_arr = res; @@ -216,6 +198,7 @@ _vec_char_pop_back(_VecCharInternal* ptr, err_t* err) if (ptr->_internal_size == 0) { __origin_override(err, "POP_BACK"); + err->flags |= ERR_SIZE_ZERO; return; } @@ -227,7 +210,6 @@ void _vec_char_reserve(_VecCharInternal* ptr, const size_t capacity, err_t* err) { err_t status = ERR_T_INIT; - char* res; if (__null_check(ptr, &status) != 0) { if ((status.flags & ERR_VEC_NULL) != 0) @@ -237,6 +219,12 @@ _vec_char_reserve(_VecCharInternal* ptr, const size_t capacity, err_t* err) return; } } + if (ptr->_internal_max_size < capacity) + { + __origin_override(err, "RESERVE"); + err->flags |= ERR_SIZE_OVERFLOW; + return; + } if (capacity == 0) { __origin_override(err, "RESERVE"); @@ -244,38 +232,19 @@ _vec_char_reserve(_VecCharInternal* ptr, const size_t capacity, err_t* err) return; } - if ((status.flags & ERR_BUF_NULL) != 0) + char* res = realloc(ptr->_internal_arr, capacity * sizeof(char)); + if (res == nullptr) { - res = calloc(capacity, sizeof(char)); - if (res == nullptr) - { - __origin_override(err, "RESERVE"); - err->flags |= ERR_ALLOC_FAIL; - return; - } - ptr->_internal_arr = res; - ptr->_internal_capacity = capacity; + __origin_override(err, "RESERVE"); + err->flags |= ERR_ALLOC_FAIL; return; } - else - { - res = realloc(ptr->_internal_arr, capacity * sizeof(char)); - if (res == nullptr) - { - __origin_override(err, "RESERVE"); - err->flags |= ERR_ALLOC_FAIL; - return; - } - ptr->_internal_arr = res; - if (ptr->_internal_size > capacity) - { - memset(&ptr->_internal_arr[capacity], 0, - ptr->_internal_size - capacity); - ptr->_internal_size = capacity; - } - } ptr->_internal_arr = res; ptr->_internal_capacity = capacity; + if (ptr->_internal_size > capacity) + { + ptr->_internal_size = capacity; + } return; } @@ -283,7 +252,6 @@ void _vec_char_shrink_to_fit(_VecCharInternal* ptr, err_t* err) { err_t status = ERR_T_INIT; - __null_check(ptr, &status); if (__null_check(ptr, &status) != 0) { __origin_override(err, "SHRINK_TO_FIT"); @@ -292,25 +260,27 @@ _vec_char_shrink_to_fit(_VecCharInternal* ptr, err_t* err) } if (ptr->_internal_size == 0) { + __origin_override(err, "SHRINK_TO_FIT"); + err->flags |= ERR_SIZE_ZERO; return; } - return _vec_char_reserve(ptr, ptr->_internal_size, err); + _vec_char_reserve(ptr, ptr->_internal_size, err); } void -_vec_char_swap(_VecCharInternal* ptr1, _VecCharInternal* ptr2, err_t* err) +_vec_char_swap(_VecCharInternal* ptr_a, _VecCharInternal* ptr_b, err_t* err) { err_t status_1 = ERR_T_INIT, status_2 = ERR_T_INIT; - if ((__null_check(ptr1, &status_1) & ERR_VEC_NULL) != 0 || - (__null_check(ptr2, &status_2) & ERR_VEC_NULL) != 0) + if ((__null_check(ptr_a, &status_1) & ERR_VEC_NULL) != 0 || + (__null_check(ptr_b, &status_2) & ERR_VEC_NULL) != 0) { __origin_override(err, "SWAP"); err->flags |= ERR_VEC_NULL; return; } - _VecCharInternal self = *ptr1; - *ptr1 = *ptr2; - *ptr2 = self; + _VecCharInternal self = *ptr_a; + *ptr_a = *ptr_b; + *ptr_b = self; return; } @@ -444,6 +414,12 @@ _vec_char_end(const _VecCharInternal* ptr, err_t* err) err->flags |= status.flags; return status.flags; } + if (ptr->_internal_size == 0) + { + __origin_override(err, "END"); + err->flags |= ERR_SIZE_ZERO; + return status.flags; + } return (size_t)(ptr->_internal_size - 1); } @@ -455,7 +431,7 @@ _vec_char_front(const _VecCharInternal* ptr, err_t* err) { __origin_override(err, "FRONT"); err->flags |= status.flags; - return status.flags; + return -1; } if (_vec_char_at(ptr, 0, err) == nullptr) { @@ -473,7 +449,7 @@ _vec_char_back(const _VecCharInternal* ptr, err_t* err) { __origin_override(err, "BACK"); err->flags |= status.flags; - return status.flags; + return -1; } if (_vec_char_at(ptr, 0, err) == nullptr) { @@ -543,7 +519,7 @@ _vec_char_erase(_VecCharInternal* ptr, const size_t iter_start, memmove(&ptr->_internal_arr[iter_start], &ptr->_internal_arr[iter_end], (ptr->_internal_size - iter_end) * sizeof(char)); memset(&ptr->_internal_arr[ptr->_internal_size - diff], 0, - sizeof(char) * ptr->_internal_size); + sizeof(char) * diff); ptr->_internal_size -= diff; return; } @@ -629,12 +605,13 @@ _vec_char_resize(_VecCharInternal* ptr, const size_t size, err_t* err) } if (size < ptr->_internal_size) { - memset(&ptr->_internal_arr[size], 0, ptr->_internal_size); + memset(&ptr->_internal_arr[size], 0, + ptr->_internal_size - size); } if (ptr->_internal_size < size && size <= ptr->_internal_capacity) { memset(&ptr->_internal_arr[ptr->_internal_size], 0, - ptr->_internal_arr[size]); + size - ptr->_internal_size); } ptr->_internal_size = size; return; @@ -715,12 +692,13 @@ _vec_char_resize(_VecCharInternal* ptr, const size_t size, const char val, } if (size < ptr->_internal_size) { - memset(&ptr->_internal_arr[size], val, ptr->_internal_size); + memset(&ptr->_internal_arr[size], val, + ptr->_internal_size - size); } if (ptr->_internal_size < size && size <= ptr->_internal_capacity) { memset(&ptr->_internal_arr[ptr->_internal_size], val, - ptr->_internal_arr[size]); + size - ptr->_internal_size); } ptr->_internal_size = size; return; @@ -767,13 +745,13 @@ _vec_char_assign(_VecCharInternal* ptr, const _VecCharInternal* iterator, } for (size_t i = iter_start; i < iter_end; i++) { - if (_vec_char_at(iterator, i, &status) != nullptr) + char* res = _vec_char_at(iterator, i, &status); + if (res == nullptr) { err->flags |= ERR_OUT_OF_BOUNDS; return; } - _vec_char_push_back(ptr, *_vec_char_at(iterator, i, &status), - &status); + _vec_char_push_back(ptr, *res, &status); if (status.flags != 0) { __origin_override(err, "ASSIGN"); @@ -793,6 +771,7 @@ _vec_char_assign(_VecCharInternal* ptr, const size_t amount, const char val, { __origin_override(err, "ASSIGN"); err->flags |= status.flags; + return; } if (amount == 0) { @@ -888,6 +867,7 @@ _vec_char_insert(_VecCharInternal* ptr, const size_t iter, const size_t amount, const char val, err_t* err) { err_t status = ERR_T_INIT; + __null_check(ptr, &status); if ((status.flags & (ERR_VEC_NULL | ERR_BUF_NULL)) != 0) { __origin_override(err, "INSERT"); @@ -900,7 +880,7 @@ _vec_char_insert(_VecCharInternal* ptr, const size_t iter, const size_t amount, err->flags |= ERR_OUT_OF_BOUNDS; return; } - if (amount <= 0) + if (amount == 0) { __origin_override(err, "INSERT"); err->flags |= ERR_SIZE_ZERO; @@ -933,7 +913,7 @@ _vec_char_insert(_VecCharInternal* ptr, const _VecCharInternal* iterator, err->flags |= status.flags; return; } - if (iter_end > iter_start || iter_end > iterator->_internal_size || + if (iter_end <= iter_start || iter_end > iterator->_internal_size || iter > ptr->_internal_size) { __origin_override(err, "INSERT"); diff --git a/src/lib/char/char.h b/src/lib/char/char.h index 92a2e87..1e3569e 100644 --- a/src/lib/char/char.h +++ b/src/lib/char/char.h @@ -13,6 +13,15 @@ typedef struct size_t _internal_max_size; } _VecCharInternal; +__attribute__((overloadable)) size_t +__null_check(const _VecCharInternal* ptr, err_t* status); + +__attribute__((overloadable)) size_t +__null_check(const char* ptr, err_t* status); + +size_t +__origin_override(err_t* status, char* str); + void _vec_char_print_vec(const _VecCharInternal* ptr, err_t* err); @@ -32,7 +41,7 @@ void _vec_char_shrink_to_fit(_VecCharInternal* ptr, err_t* err); void -_vec_char_swap(_VecCharInternal* ptr, _VecCharInternal* ptr_other, err_t* err); +_vec_char_swap(_VecCharInternal* ptr_a, _VecCharInternal* ptr_b, err_t* err); const char* _vec_char_data(const _VecCharInternal* ptr, err_t* err); diff --git a/src/lib/utils/utils.c b/src/lib/utils/utils.c index 5a8e497..cf61493 100644 --- a/src/lib/utils/utils.c +++ b/src/lib/utils/utils.c @@ -12,7 +12,7 @@ vec_get_error_desc(ERROR_CODES err) ERROR_LIST(X) #undef X default: - "Unknown"; + return "Unknown"; } } diff --git a/src/main.c b/src/main.c index 4b3aab6..baecdc5 100644 --- a/src/main.c +++ b/src/main.c @@ -34,8 +34,8 @@ main() // // Your pop back is a great example of how to handle it and is a clean // if statement as a header to all functions. - char res[4] = { 'a', 'b', 'c', 'd' }; - err_t status = ERR_T_INIT, err = ERR_T_INIT; + // char res[4] = { 'a', 'b', 'c', 'd' }; + err_t status = ERR_T_INIT; status.flags |= ERR_BUF_NULL | ERR_VEC_NULL | ERR_STR_NULL; unsigned int mask = 0; diff --git a/tests/fuzz_char.c b/tests/fuzz_char.c new file mode 100644 index 0000000..176f6a9 --- /dev/null +++ b/tests/fuzz_char.c @@ -0,0 +1,46 @@ +#include +#include + +#include "char/char.h" +#include "utils/utils.h" + +int +LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + if (size < 2) return 0; + + err_t err = ERR_T_INIT; + _VecCharInternal vec = VEC_INIT; + + // Use input bytes to drive arbitrary vector operations + for (size_t i = 0; i < size; i++) + { + uint8_t op = data[i] % 5; + switch (op) + { + case 0: + _vec_char_push_back(&vec, (char)data[i], &err); + break; + case 1: + _vec_char_pop_back(&vec, &err); + break; + case 2: + if (vec._internal_size > 0) + { + size_t idx = (size_t)data[i] % + vec._internal_size; + _vec_char_erase(&vec, idx, &err); + } + break; + case 3: + _vec_char_reserve(&vec, (size_t)data[i], &err); + break; + case 4: + _vec_char_shrink_to_fit(&vec, &err); + break; + } + } + + _vec_char_delete(&vec, &err); + return 0; +} diff --git a/tests/test_char.c b/tests/test_char.c new file mode 100644 index 0000000..e688986 --- /dev/null +++ b/tests/test_char.c @@ -0,0 +1,104 @@ +#include +#include + +#include "char/char.h" +#include "utils/utils.h" + +static void +test_empty_vector_underflows(void) +{ + printf("[RUN] Testing empty vector boundaries & underflows...\n"); + err_t err = ERR_T_INIT; + _VecCharInternal vec = VEC_INIT; + + // 1. Pop back on an empty container must not underflow size_t (0 - 1) + _vec_char_pop_back(&vec, &err); + assert((err.flags & ERR_SIZE_ZERO) != 0 || err.flags != 0); + + // 2. Reading back/end of an empty container + err = (err_t)ERR_T_INIT; + _vec_char_back(&vec, &err); + assert(err.flags != 0); + + // 3. Reserving 0 capacity + err = (err_t)ERR_T_INIT; + _vec_char_reserve(&vec, 0, &err); + assert((err.flags & ERR_SIZE_ZERO) != 0); + + _vec_char_delete(&vec, &err); + printf("[PASS] Empty vector boundaries handled.\n"); +} + +static void +test_push_growth_and_shrink(void) +{ + printf("[RUN] Testing push, capacity growth, and indexing...\n"); + err_t err = ERR_T_INIT; + _VecCharInternal vec = VEC_INIT; + + // Push elements to trigger initial malloc then realloc + for (char c = 'a'; c <= 'z'; c++) + { + _vec_char_push_back(&vec, c, &err); + assert(err.flags == 0); + } + assert(vec._internal_size == 26); + assert(vec._internal_capacity >= 26); + + // Verify boundary indexing + assert(_vec_char_front(&vec, &err) == 'a'); + assert(_vec_char_back(&vec, &err) == 'z'); + + // Index out of bounds check + err = (err_t)ERR_T_INIT; + _vec_char_at(&vec, 26, &err); + assert(err.flags != 0); + + // Shrink to fit verification + err = (err_t)ERR_T_INIT; + _vec_char_shrink_to_fit(&vec, &err); + assert(vec._internal_capacity == 26); + + _vec_char_delete(&vec, &err); + printf("[PASS] Growth and indexing passed.\n"); +} + +static void +test_range_insert_and_erase(void) +{ + printf("[RUN] Testing range insertions and erasures...\n"); + err_t err = ERR_T_INIT; + _VecCharInternal vec = VEC_INIT; + + _vec_char_push_back(&vec, 'A', &err); + _vec_char_push_back(&vec, 'B', &err); + _vec_char_push_back(&vec, 'E', &err); + + // Single insert between 'B' and 'E' at index 2 + _vec_char_insert(&vec, 2, 'C', &err); + assert(vec._internal_size == 4); + assert(*_vec_char_at(&vec, 2, &err) == 'C'); + + // Erase test + _vec_char_erase(&vec, 2, &err); + assert(vec._internal_size == 3); + assert(*_vec_char_at(&vec, 2, &err) == 'E'); + + // Inverted range check: iter_end <= iter_start must report error + err = (err_t)ERR_T_INIT; + _vec_char_erase(&vec, 2, 1, &err); + assert(err.flags != 0); + + _vec_char_delete(&vec, &err); + printf("[PASS] Range insert and erase passed.\n"); +} + +int +main(void) +{ + test_empty_vector_underflows(); + test_push_growth_and_shrink(); + test_range_insert_and_erase(); + printf("\nAll char vector edge-case tests passed cleanly!\n"); + return 0; +} diff --git a/tests/test_learn.c b/tests/test_learn.c new file mode 100644 index 0000000..03f0dfa --- /dev/null +++ b/tests/test_learn.c @@ -0,0 +1,686 @@ +#include +#include +#include +#include +#include +#include +#include + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +const char* +__asan_default_options(void) +{ + return "allocator_may_return_null=1"; +} +#endif +#endif + +/* ========================================================================= + * 1. BASIC LIFECYCLE & NULL GUARDS + * ========================================================================= */ + +static void +test_push_back_single(void) +{ + printf("[1] Testing push_back with a single character...\n"); + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + _vec_char_push_back(&vec, 'A', &err); + + assert(err.flags == 0); + assert(vec._internal_size == 1); + assert(vec._internal_arr[0] == 'A'); + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +static void +test_null_guards(void) +{ + printf("[2] Testing null pointer checks in __null_check...\n"); + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + size_t res1 = __null_check(&vec, nullptr); + assert(res1 == ERR_ERR_T_NULL); + + size_t res2 = __null_check((const _VecCharInternal*)nullptr, &err); + assert(res2 == ERR_VEC_NULL); + assert((err.flags & ERR_VEC_NULL) != 0); + + err = (err_t)ERR_T_INIT; + size_t res3 = __null_check(&vec, &err); + assert(res3 == ERR_BUF_NULL); + assert((err.flags & ERR_BUF_NULL) != 0); + + err = (err_t)ERR_T_INIT; + size_t res4 = __null_check(vec._internal_arr, &err); + assert(res4 == ERR_BUF_NULL); + assert((err.flags & ERR_BUF_NULL) != 0); + + err = (err_t)ERR_T_INIT; + size_t res5 = __null_check((const char*)nullptr, nullptr); + assert(res5 == ERR_ERR_T_NULL); + + printf("Passed\n"); +} + +static void +test_origin_override(void) +{ + printf("[3] Testing __origin_override...\n"); + err_t err = ERR_T_INIT; + + size_t res_null_status = __origin_override(nullptr, "TEST"); + assert(res_null_status == ERR_ERR_T_NULL); + + size_t res = __origin_override(&err, nullptr); + assert(res == ERR_STR_NULL); + assert((err.flags & ERR_STR_NULL) != 0); + + err = (err_t)ERR_T_INIT; + __origin_override(&err, "INITIAL"); + assert(strcmp(err.origin, "INITIAL") == 0); + + // Override existing origin -> ERR_ORIGIN_UNALTERABLE + __origin_override(&err, "REPLACED"); + assert((err.flags & ERR_ORIGIN_UNALTERABLE) != 0); + assert(strcmp(err.origin, "INITIAL") == 0); + + printf("Passed\n"); +} + +/* ========================================================================= + * 2. PRINT & COPY + * ========================================================================= */ + +static void +test_print_vec(void) +{ + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + printf("[4] Testing print_vec with null vector\n"); + _vec_char_print_vec(nullptr, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + printf("Passed\n"); + + printf("[5] Testing print_vec with null array\n"); + err = (err_t)ERR_T_INIT; + _vec_char_print_vec(&vec, &err); + assert((err.flags & ERR_BUF_NULL) != 0); + printf("Passed\n"); + + printf("[6] Testing print_vec with null err\n"); + _vec_char_print_vec(&vec, nullptr); + printf("Passed\n"); + + printf("[7] Testing print_vec with valid parameters\n"); + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&vec, 'a', &err); + _vec_char_push_back(&vec, 'b', &err); + _vec_char_print_vec(&vec, &err); + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +static void +test_cpy(void) +{ + char arr[3] = { 'a', 'b', 'c' }; + _VecCharInternal blank = VEC_INIT; + + printf("[8] Testing _vec_char_cpy with null source arr\n"); + err_t err1 = ERR_T_INIT; + _VecCharInternal tmp = _vec_char_cpy(nullptr, 3, &err1); + assert(blank._internal_arr == tmp._internal_arr && + blank._internal_capacity == tmp._internal_capacity && + blank._internal_size == tmp._internal_size && + blank._internal_max_size == tmp._internal_max_size); + assert((err1.flags & ERR_BUF_NULL) != 0); + printf("Passed\n"); + + printf("[9] Testing _vec_char_cpy with null err\n"); + _VecCharInternal res2 = _vec_char_cpy(arr, 3, nullptr); + assert(blank._internal_arr == res2._internal_arr && + blank._internal_capacity == res2._internal_capacity && + blank._internal_size == res2._internal_size && + blank._internal_max_size == res2._internal_max_size); + printf("Passed\n"); + + printf("[10] Testing _vec_char_cpy with size 0\n"); + err_t err3 = ERR_T_INIT; + _VecCharInternal res3 = _vec_char_cpy(arr, 0, &err3); + assert(blank._internal_arr == res3._internal_arr && + blank._internal_capacity == res3._internal_capacity && + blank._internal_size == res3._internal_size && + blank._internal_max_size == res3._internal_max_size); + assert((err3.flags & ERR_SIZE_ZERO) != 0); + printf("Passed\n"); + + printf("[11] Testing _vec_char_cpy with size exceeding max_size\n"); + err_t err4 = ERR_T_INIT; + _VecCharInternal res4 = + _vec_char_cpy(arr, res3._internal_max_size + 1, &err4); + assert(blank._internal_arr == res4._internal_arr && + blank._internal_capacity == res4._internal_capacity && + blank._internal_size == res4._internal_size && + blank._internal_max_size == res4._internal_max_size); + assert((err4.flags & ERR_SIZE_OVERFLOW) != 0); + printf("Passed\n"); + + printf("[12] Testing _vec_char_cpy allocation failure\n"); + err_t err5 = ERR_T_INIT; + size_t huge_size = ((size_t)1 << 40); + _VecCharInternal res5 = _vec_char_cpy(arr, huge_size, &err5); + assert((err5.flags & ERR_ALLOC_FAIL) != 0); + printf("Passed\n"); + + printf("[13] Testing _vec_char_cpy with valid inputs\n"); + err_t err = ERR_T_INIT; + char res[4] = { 'a', 'b', 'c', 'd' }; + _VecCharInternal vec = _vec_char_cpy(res, 4, &err); + assert(err.flags == 0); + assert(vec._internal_size == 4); + assert(vec._internal_capacity == 4); + assert(memcmp(vec._internal_arr, res, 4) == 0); + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +/* ========================================================================= + * 3. PUSH, POP, CAPACITY & SIZING + * ========================================================================= */ + +static void +test_push_back(void) +{ + printf("[14] Testing _vec_char_push_back null vec\n"); + err_t err = ERR_T_INIT; + _vec_char_push_back(nullptr, 'a', &err); + assert((err.flags & ERR_VEC_NULL) != 0); + printf("Passed\n"); + + printf("[15] Testing _vec_char_push_back null err\n"); + _VecCharInternal vec1 = VEC_INIT; + _vec_char_push_back(&vec1, 'a', nullptr); + assert(vec1._internal_size == 1); + _vec_char_delete(&vec1, &err); + printf("Passed\n"); + + printf("[16] Testing _vec_char_push_back size overflow\n"); + _VecCharInternal vec2 = VEC_INIT; + err_t err2 = ERR_T_INIT; + vec2._internal_size = vec2._internal_max_size; + _vec_char_push_back(&vec2, 'a', &err2); + assert((err2.flags & ERR_SIZE_OVERFLOW) != 0); + printf("Passed\n"); + + printf("[17] Testing push_back capacity growth realloc\n"); + err_t err3 = ERR_T_INIT; + _VecCharInternal vec3 = VEC_INIT; + for (char c = '0'; c <= '9'; c++) + { + _vec_char_push_back(&vec3, c, &err3); + assert(err3.flags == 0); + } + assert(vec3._internal_size == 10); + assert(vec3._internal_capacity >= 10); + _vec_char_delete(&vec3, &err3); + printf("Passed\n"); +} + +static void +test_pop_back(void) +{ + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + printf("[18] Testing _vec_char_pop_back null vector\n"); + _vec_char_pop_back(nullptr, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + printf("Passed\n"); + + printf("[19] Testing _vec_char_pop_back size 0\n"); + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&vec, 'x', &err); + _vec_char_pop_back(&vec, + &err); // size is now 0, but buffer is allocated + err = (err_t)ERR_T_INIT; + _vec_char_pop_back(&vec, &err); + assert((err.flags & ERR_SIZE_ZERO) != 0); + printf("Passed\n"); + + printf("[20] Testing _vec_char_pop_back valid pop\n"); + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&vec, 'a', &err); + _vec_char_push_back(&vec, 'b', &err); + assert(vec._internal_size == 2); + _vec_char_pop_back(&vec, &err); + assert(vec._internal_size == 1); + assert(vec._internal_arr[0] == 'a'); + assert(vec._internal_arr[1] == 0); + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +static void +test_reserve(void) +{ + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + printf("[21] Testing _vec_char_reserve null vector\n"); + _vec_char_reserve(nullptr, 1, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + printf("Passed\n"); + + printf("[22] Testing _vec_char_reserve capacity 0\n"); + err = (err_t)ERR_T_INIT; + _vec_char_reserve(&vec, 0, &err); + assert((err.flags & ERR_SIZE_ZERO) != 0); + printf("Passed\n"); + + printf("[23] Testing _vec_char_reserve calloc on unallocated buffer\n"); + err = (err_t)ERR_T_INIT; + _vec_char_reserve(&vec, 32, &err); + assert(err.flags == 0); + assert(vec._internal_capacity == 32); + assert(vec._internal_arr != nullptr); + printf("Passed\n"); + + printf("[24] Testing _vec_char_reserve realloc to larger capacity\n"); + _vec_char_reserve(&vec, 64, &err); + assert(err.flags == 0); + assert(vec._internal_capacity == 64); + printf("Passed\n"); + + printf("[25] Testing _vec_char_reserve truncating size\n"); + vec._internal_size = 50; + _vec_char_reserve(&vec, 20, &err); + assert(vec._internal_size == 20); + assert(vec._internal_capacity == 20); + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +static void +test_shrink_to_fit(void) +{ + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + printf("[26] Testing _vec_char_shrink_to_fit null vector\n"); + _vec_char_shrink_to_fit(nullptr, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + printf("Passed\n"); + + printf( + "[27] Testing _vec_char_shrink_to_fit size 0 with allocated " + "buffer\n"); + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&vec, 'a', &err); + _vec_char_pop_back(&vec, &err); + err = (err_t)ERR_T_INIT; + _vec_char_shrink_to_fit(&vec, &err); + assert((err.flags & ERR_SIZE_ZERO) != 0); + printf("Passed\n"); + + printf("[28] Testing _vec_char_shrink_to_fit normal shrinkage\n"); + _vec_char_reserve(&vec, 128, &err); + for (char c = '0'; c < '5'; c++) + { + _vec_char_push_back(&vec, c, &err); + } + assert(vec._internal_capacity == 128); + assert(vec._internal_size == 5); + err = (err_t)ERR_T_INIT; + _vec_char_shrink_to_fit(&vec, &err); + assert(err.flags == 0); + assert(vec._internal_capacity == 5); + assert(vec._internal_size == 5); + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +/* ========================================================================= + * 4. GETTERS, ELEMENT ACCESS & CLEAR + * ========================================================================= */ + +static void +test_getters_and_access(void) +{ + printf("[29] Testing getters, clear, and boundary element access...\n"); + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + // 1. Null pointer check + assert(_vec_char_data(nullptr, &err) == nullptr); + assert((err.flags & ERR_VEC_NULL) != 0); + + err = (err_t)ERR_T_INIT; + assert(_vec_char_size(nullptr, &err) == 0); + assert((err.flags & ERR_VEC_NULL) != 0); + + err = (err_t)ERR_T_INIT; + assert(_vec_char_capacity(nullptr, &err) == 0); + assert((err.flags & ERR_VEC_NULL) != 0); + + err = (err_t)ERR_T_INIT; + assert(_vec_char_max_size(nullptr, &err) == 0); + assert((err.flags & ERR_VEC_NULL) != 0); + + // 2. Unallocated buffer check for empty() + err = (err_t)ERR_T_INIT; + size_t unalloc_empty = _vec_char_empty(&vec, &err); + assert((err.flags & ERR_BUF_NULL) != 0); + assert(unalloc_empty != 0); + + // 3. Allocate the buffer and pop so size is 0 with valid arr + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&vec, 'x', &err); + _vec_char_pop_back(&vec, &err); + err = (err_t)ERR_T_INIT; + + // Now _internal_arr is non-null and size is 0: + assert(_vec_char_empty(&vec, &err) == 0); + + // Boundary element access on size 0 + err = (err_t)ERR_T_INIT; + assert(_vec_char_front(&vec, &err) == 0 || + (err.flags & ERR_OUT_OF_BOUNDS) != 0); + + err = (err_t)ERR_T_INIT; + assert(_vec_char_back(&vec, &err) == 0 || + (err.flags & ERR_OUT_OF_BOUNDS) != 0); + + // 4. Populate container + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&vec, 'x', &err); + _vec_char_push_back(&vec, 'y', &err); + _vec_char_push_back(&vec, 'z', &err); + + assert(_vec_char_size(&vec, &err) == 3); + assert(_vec_char_capacity(&vec, &err) >= 3); + assert(_vec_char_data(&vec, &err) == vec._internal_arr); + assert(_vec_char_front(&vec, &err) == 'x'); + assert(_vec_char_back(&vec, &err) == 'z'); + assert(_vec_char_begin(&vec, &err) == 0); + assert(_vec_char_end(&vec, &err) == 2); + + // Out of bounds at() + err = (err_t)ERR_T_INIT; + assert(_vec_char_at(&vec, 10, &err) == nullptr); + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + // Clear container + err = (err_t)ERR_T_INIT; + _vec_char_clear(&vec, &err); + assert(vec._internal_size == 0); + assert(vec._internal_arr[0] == 0); + + _vec_char_delete(&vec, &err); + assert(vec._internal_arr == nullptr); + assert(vec._internal_size == 0); + assert(vec._internal_capacity == 0); + + printf("Passed\n"); +} + +/* ========================================================================= + * 5. SWAP, ERASE, RESIZE, ASSIGN & INSERT + * ========================================================================= */ + +static void +test_swap(void) +{ + printf("[30] Testing _vec_char_swap...\n"); + _VecCharInternal a = VEC_INIT; + _VecCharInternal b = VEC_INIT; + err_t err = ERR_T_INIT; + + // Null vector checks + _vec_char_swap(nullptr, &b, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + + err = (err_t)ERR_T_INIT; + _vec_char_swap(&a, nullptr, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + + // Normal swap + err = (err_t)ERR_T_INIT; + _vec_char_push_back(&a, 'A', &err); + _vec_char_push_back(&b, 'B', &err); + _vec_char_push_back(&b, 'C', &err); + + _vec_char_swap(&a, &b, &err); + assert(a._internal_size == 2); + assert(a._internal_arr[0] == 'B'); + assert(b._internal_size == 1); + assert(b._internal_arr[0] == 'A'); + + _vec_char_delete(&a, &err); + _vec_char_delete(&b, &err); + printf("Passed\n"); +} + +static void +test_erase_variants(void) +{ + printf("[31] Testing _vec_char_erase single & range overloads...\n"); + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + for (char c = '0'; c <= '5'; c++) + { + _vec_char_push_back(&vec, c, &err); + } // "012345" + + // Single element erase out of bounds (noop) + _vec_char_erase(&vec, 10, &err); + assert(vec._internal_size == 6); + + // Erase index 2 ('2') -> "01345" + _vec_char_erase(&vec, 2, &err); + assert(vec._internal_size == 5); + assert(vec._internal_arr[2] == '3'); + + // Range erase invalid bounds + err = (err_t)ERR_T_INIT; + _vec_char_erase(&vec, 4, 2, &err); // iter_end <= iter_start + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + err = (err_t)ERR_T_INIT; + _vec_char_erase(&vec, 0, 10, &err); // iter_end > size + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + // Range erase middle: index 1 through 3 ("0[13]45" -> "045") + err = (err_t)ERR_T_INIT; + _vec_char_erase(&vec, 1, 3, &err); + assert(vec._internal_size == 3); + assert(vec._internal_arr[0] == '0'); + assert(vec._internal_arr[1] == '4'); + assert(vec._internal_arr[2] == '5'); + + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +static void +test_resize(void) +{ + printf("[32] Testing _vec_char_resize (zero-fill & value-fill)...\n"); + _VecCharInternal vec = VEC_INIT; + err_t err = ERR_T_INIT; + + // Null vector + _vec_char_resize((_VecCharInternal*)nullptr, 10, &err); + assert((err.flags & ERR_VEC_NULL) != 0); + + // Resize unallocated buffer to size 5 + err = (err_t)ERR_T_INIT; + _vec_char_resize(&vec, 5, &err); + assert(err.flags == 0); + assert(vec._internal_size == 5); + assert(vec._internal_arr[0] == 0); + + // Same size requested + err = (err_t)ERR_T_INIT; + _vec_char_resize(&vec, 5, &err); + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + // Exceeds max size + err = (err_t)ERR_T_INIT; + _vec_char_resize(&vec, vec._internal_max_size + 1, &err); + assert((err.flags & ERR_SIZE_OVERFLOW) != 0); + + // Grow with value fill + err = (err_t)ERR_T_INIT; + _vec_char_resize(&vec, 8, 'Z', &err); + assert(vec._internal_size == 8); + assert(vec._internal_arr[5] == 'Z'); + assert(vec._internal_arr[7] == 'Z'); + + // Shrink with value fill + err = (err_t)ERR_T_INIT; + _vec_char_resize(&vec, 3, 'A', &err); + assert(vec._internal_size == 3); + + _vec_char_delete(&vec, &err); + printf("Passed\n"); +} + +static void +test_assign(void) +{ + printf("[33] Testing _vec_char_assign overloads...\n"); + _VecCharInternal dst = VEC_INIT; + _VecCharInternal src = VEC_INIT; + err_t err = ERR_T_INIT; + + // Populate src: 'A', 'B', 'C', 'D', 'E' + for (char c = 'A'; c <= 'E'; c++) + { + _vec_char_push_back(&src, c, &err); + } + + // Allocate dst buffer first so it is not unallocated + _vec_char_push_back(&dst, 'z', &err); + err = (err_t)ERR_T_INIT; + + // 1. Fill assign: 4 'X's + _vec_char_assign(&dst, 4, 'X', &err); + assert(err.flags == 0); + assert(dst._internal_size == 4); + assert(dst._internal_arr[0] == 'X'); + assert(dst._internal_arr[3] == 'X'); + + // 2. Fill assign with 0 amount + err = (err_t)ERR_T_INIT; + _vec_char_assign(&dst, 0, 'Y', &err); + assert((err.flags & ERR_SIZE_ZERO) != 0); + + // 3. Range assign self-buffer check + err = (err_t)ERR_T_INIT; + _vec_char_assign(&dst, &dst, 0, 2, &err); + assert((err.flags & ERR_DOUBLE_BUF) != 0); + + // 4. Range assign invalid sub-range (start >= end) + err = (err_t)ERR_T_INIT; + _vec_char_assign(&dst, &src, 3, 2, &err); + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + // 5. Range assign out of bounds end + err = (err_t)ERR_T_INIT; + _vec_char_assign(&dst, &src, 0, 10, &err); + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + _vec_char_delete(&dst, &err); + _vec_char_delete(&src, &err); + printf("Passed\n"); +} + +static void +test_insert(void) +{ + printf("[34] Testing _vec_char_insert overloads...\n"); + _VecCharInternal vec = VEC_INIT; + _VecCharInternal other = VEC_INIT; + err_t err = ERR_T_INIT; + + // Single insert on unallocated buffer + _vec_char_insert(&vec, 0, 'M', &err); + assert(vec._internal_size == 1); + assert(vec._internal_arr[0] == 'M'); + + // Single insert out of bounds + err = (err_t)ERR_T_INIT; + _vec_char_insert(&vec, 5, 'O', &err); + assert((err.flags & ERR_OUT_OF_BOUNDS) != 0); + + // Single insert at middle: index 0 ('A', then 'M' shifts to 1) + err = (err_t)ERR_T_INIT; + _vec_char_insert(&vec, 0, 'A', &err); + assert(vec._internal_size == 2); + assert(vec._internal_arr[0] == 'A'); + assert(vec._internal_arr[1] == 'M'); + + // Repeated insert: insert 3 'B's at index 1 -> 'A', 'B', 'B', 'B', 'M' + _vec_char_insert(&vec, 1, 3, 'B', &err); + assert(vec._internal_size == 5); + assert(vec._internal_arr[1] == 'B'); + assert(vec._internal_arr[3] == 'B'); + assert(vec._internal_arr[4] == 'M'); + + // Repeated insert amount 0 + err = (err_t)ERR_T_INIT; + _vec_char_insert(&vec, 0, 0, 'Z', &err); + assert((err.flags & ERR_SIZE_ZERO) != 0); + + // Vector-from-vector range insert + _vec_char_push_back(&other, '1', &err); + _vec_char_push_back(&other, '2', &err); + + // Double buffer check + err = (err_t)ERR_T_INIT; + _vec_char_insert(&vec, &vec, 0, 0, 1, &err); + assert((err.flags & ERR_DOUBLE_BUF) != 0); + + _vec_char_delete(&vec, &err); + _vec_char_delete(&other, &err); + printf("Passed\n"); +} + +/* ========================================================================= + * MAIN TEST RUNNER + * ========================================================================= */ + +int +main(void) +{ + printf("=========================================\n"); + printf(" Starting Complete libvec Unit Test Suite\n"); + printf("=========================================\n"); + + test_push_back_single(); + test_null_guards(); + test_origin_override(); + test_print_vec(); + test_cpy(); + test_push_back(); + test_pop_back(); + test_reserve(); + test_shrink_to_fit(); + test_getters_and_access(); + test_swap(); + test_erase_variants(); + test_resize(); + test_assign(); + test_insert(); + + printf("=========================================\n"); + printf(" All 34 Test Suites Executed Successfully!\n"); + printf("=========================================\n"); + return 0; +}