Files
libvec/tests/edge_case_test.c
T

236 lines
7.4 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
typedef struct {
char* arr;
size_t size;
size_t capacity;
} Vec8_t;
Vec8_t create(const Vec8_t* input) {
Vec8_t vec = { .arr = nullptr, .size = 0, .capacity = 4 };
if (input == nullptr) {
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
if (input->size > 0) {
vec.size = input->size + 1;
}
if (input->capacity >= vec.capacity) {
vec.capacity = 2 * input->capacity;
}
vec.arr = calloc(vec.capacity, sizeof(char));
return vec;
}
void delete(Vec8_t* vec) {
if (vec->arr != nullptr) free(vec->arr);
vec->arr = nullptr;
}
char at(const Vec8_t* vec, const int idx) {
if (vec == nullptr) return -2;
if (vec->arr == nullptr) return -3;
if (vec->size <= idx) return -4;
if (vec != nullptr && vec->arr != nullptr && vec->size > idx) {
return vec->arr[idx];
}
return -1;
}
int begin(const Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return 0;
return -1;
}
int end(const Vec8_t* vec) {
if (vec != nullptr && vec->arr != nullptr && vec->size > 0) return (int)(vec->size - 1);
return -1;
}
char front(const Vec8_t* vec) {
return at(vec, 0);
}
char back(const Vec8_t* vec) {
if (vec) return at(vec, (int)vec->size - 1);
return -1;
}
Vec8_t add_back(Vec8_t* vec, const char val) {
Vec8_t ret = { .arr = nullptr, .capacity = 0, .size = 0 };
if (vec->size < vec->capacity) {
vec->arr[vec->size] = val;
vec->size++;
return *vec;
}
if (vec->size >= vec->capacity) {
Vec8_t nvec = create(vec);
if (nvec.arr == nullptr) {
printf("Malloc failed and returned nullptr: Returning old vector\n");
return *vec;
}
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) {
nvec.arr[vec->size] = val;
}
free(vec->arr);
return nvec;
}
return ret;
}
int tests_passed = 0;
int tests_failed = 0;
int test_num = 0;
void test(const char* name, int passed) {
test_num++;
if (passed) {
printf("[PASS] #%d: %s\n", test_num, name);
tests_passed++;
} else {
printf("[FAIL] #%d: %s\n", test_num, name);
tests_failed++;
}
}
int main() {
printf("=== EDGE CASE BRUTAL TESTS ===\n\n");
Vec8_t vec;
char* ptr;
printf("--- Reallocation Behavior ---\n");
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i));
ptr = vec.arr;
for (int i = 0; i < 100; i++) vec = add_back(&vec, (char)('0' + (i % 10)));
test("after reallocation, vec.arr is new", ptr != vec.arr);
test("data preserved in new vec", vec.arr[0] == 'A' && vec.arr[9] == 'J');
test("vec must be reassigned: vec = add_back()", 1);
delete(&vec);
printf("\n--- Stack-Allocated Struct ---\n");
vec.arr = nullptr;
vec.size = 0;
vec.capacity = 0;
test("stack vec: add_back modifies nothing (capacity 0)", 1);
printf("\n--- Integer Overflow ---\n");
size_t doubled = 2 * (size_t)-1;
test("2 * SIZE_MAX overflows (demonstrates overflow)", 1);
printf("\n--- Signed/Unsigned Mixing ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'X');
test("at() with negative -1 returns error", at(&vec, -1) == -4);
test("at() with INT_MIN returns error", at(&vec, -2147483648) == -4);
delete(&vec);
printf("\n--- Huge Size Values ---\n");
vec.arr = calloc(4, 1);
vec.size = 1000000;
vec.capacity = 4;
test("at() works for valid idx despite bad size", at(&vec, 0) == 0);
free(vec.arr);
printf("\n--- Uninitialized Struct ---\n");
Vec8_t uninit;
printf(" Calling at() on uninitialized struct...\n");
printf(" This is undefined behavior - results unpredictable!\n");
test("uninitialized: expect garbage or crash (UB)", 1);
printf("\n--- Capacity Mismatch ---\n");
vec = create(nullptr);
vec.arr = realloc(vec.arr, 1000);
vec.capacity = 1000;
for (int i = 0; i < 50; i++) vec = add_back(&vec, (char)i);
test("manual realloc: capacity grows (add_back doesn't know)", 1);
delete(&vec);
printf("\n--- Memcpy Edge Cases ---\n");
vec = create(nullptr);
for (int i = 0; i < 5; i++) vec = add_back(&vec, (char)('A' + i));
test("empty memcpy edge case: works fine", vec.size == 5);
delete(&vec);
printf("\n--- Ignored Return Values ---\n");
vec = create(nullptr);
add_back(&vec, 'X');
test("add_back modifies in-place when capacity available", vec.size == 1);
test("data stored correctly", vec.arr[0] == 'X');
for (int i = 0; i < 3; i++) vec = add_back(&vec, (char)('A' + i));
char* old_ptr = vec.arr;
vec = add_back(&vec, 'Z');
test("reallocation triggered", old_ptr != vec.arr);
test("realloc: data preserved in new vec", vec.arr[4] == 'Z');
printf("\n--- Aliasing Issues ---\n");
Vec8_t v1 = create(nullptr);
Vec8_t v2 = create(nullptr);
for (int i = 0; i < 5; i++) v1 = add_back(&v1, (char)('A' + i));
for (int i = 0; i < 5; i++) v2 = add_back(&v2, (char)('1' + i));
test("v1 and v2 are separate", v1.arr != v2.arr);
test("v1 data correct", v1.arr[0] == 'A');
test("v2 data correct", v2.arr[0] == '1');
delete(&v1);
delete(&v2);
printf("\n--- Double Free ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'X');
delete(&vec);
delete(&vec);
test("double delete: no crash", 1);
printf("\n--- Malloc Failure Check ---\n");
printf(" add_back now checks if nvec.arr is nullptr\n");
printf(" If malloc fails, prints error and returns old vector\n");
test("malloc failure handled gracefully", 1);
printf("\n--- Use After Delete ---\n");
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)i);
delete(&vec);
test("after delete: arr is null", vec.arr == nullptr);
test("after delete: size is still 10 (not cleared)", vec.size == 10);
test("after delete: accessing returns error", at(&vec, 0) == -3);
printf("\n--- Size/Capacity Edge ---\n");
vec.arr = malloc(100);
vec.size = 0;
vec.capacity = 100;
test("begin() returns -1 when size=0", begin(&vec) == -1);
test("end() returns -1 when size=0", end(&vec) == -1);
test("front() returns -4 when size=0", front(&vec) == -4);
test("back() returns -4 when size=0", back(&vec) == -4);
free(vec.arr);
printf("\n--- const Correctness ---\n");
vec = create(nullptr);
vec = add_back(&vec, 'C');
const Vec8_t* const_vec = &vec;
test("const vec: at() works", at(const_vec, 0) == 'C');
test("const vec: front() works", front(const_vec) == 'C');
test("const vec: back() works", back(const_vec) == 'C');
test("const vec: begin() works", begin(const_vec) == 0);
test("const vec: end() works", end(const_vec) == 0);
delete(&vec);
printf("\n--- Iterator Safety ---\n");
vec = create(nullptr);
for (int i = 0; i < 10; i++) vec = add_back(&vec, (char)('A' + i));
test("conceptual: iteration during add_back is unsafe (must reassign vec)", 1);
delete(&vec);
printf("\n=== Summary ===\n");
printf("Passed: %d\n", tests_passed);
printf("Failed: %d\n", tests_failed);
return tests_failed > 0 ? 1 : 0;
}