This repository has been archived on 2025-02-04. You can view files and clone it, but cannot push or open issues or pull requests.
helpless/oogabooga/tests.c
Charlie db73d0bd2e - Basic fixed-length string with replacement for C standard printing & fmt stuff
- Jai-like print procedures
	- %s formats 'string' and to format a char* you do %cs
	- It detects if %cs pointer is outside of program memory or stack and asserts
	- AND same for if a char* is passed to %s
	- Print directly writes to stdout without any allocations
	- Basic utlity procedures
	- Buncha string tests
- Beef up assert to display file & line as well
- Don't define memcpy procedures if compiler has intrinsics for them
- Init memory arena for allocations in initialization time before heap is ready (stack memory)
- os_compare_and_swap
- Spinlock "primitive" (wrapper around a bool using compare_and_swap)
- Switched to using spinlock instead of os mutex in heap for synchronization.
- is_pointer_valid() which checks if address is in program_memory or thread stack
2024-06-28 18:50:30 +02:00

381 lines
No EOL
11 KiB
C

void log_heap() {
os_spinlock_lock(heap_lock);
printf("\nHEAP:\n");
Heap_Block *block = heap_head;
while (block != 0) {
printf("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size);
Heap_Free_Node *node = block->free_head;
u64 total_free = 0;
while (node != 0) {
printf("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size);
total_free += node->size;
node = node->next;
}
printf("\t TOTAL FREE: %llu\n\n", total_free);
block = block->next;
}
os_spinlock_unlock(heap_lock);
}
void test_allocator(bool do_log_heap) {
// Basic allocation and free
int* a = (int*)alloc(sizeof(int));
int* b = (int*)alloc(sizeof(int));
int* c = (int*)alloc(sizeof(int));
*a = 69;
*b = 420;
*c = 1337;
assert(*a == 69, "Test failed: Memory corrupted");
assert(*b == 420, "Test failed: Memory corrupted");
assert(*c == 1337, "Test failed: Memory corrupted");
// Test growing memory
os_grow_program_memory(1024 * 1024 * 1000);
assert(*a == 69, "Test failed: Memory corrupted");
assert(*b == 420, "Test failed: Memory corrupted");
assert(*c == 1337, "Test failed: Memory corrupted");
// Allocate and free large block
void* large_block = alloc(1024 * 1024 * 100);
dealloc(large_block);
// Allocate multiple small blocks
void* blocks[100];
for (int i = 0; i < 100; ++i) {
blocks[i] = alloc(128);
assert(blocks[i] != NULL, "Failed to allocate small block");
}
for (int i = 0; i < 100; ++i) {
dealloc(blocks[i]);
}
// Stress test with various sizes
for (int i = 1; i <= 1000; ++i) {
void* p = alloc(i * 64);
assert(p != NULL, "Failed to allocate varying size block");
dealloc(p);
}
// Free in reverse order
for (int i = 0; i < 100; ++i) {
blocks[i] = alloc(128);
assert(blocks[i] != NULL, "Failed to allocate small block");
}
for (int i = 99; i >= 0; --i) {
dealloc(blocks[i]);
}
// Test memory integrity with various allocation patterns
int* nums[10];
for (int i = 0; i < 10; ++i) {
nums[i] = (int*)alloc(sizeof(int) * 10);
for (int j = 0; j < 10; ++j) {
nums[i][j] = i * 10 + j;
}
}
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
assert(nums[i][j] == i * 10 + j, "Memory corruption detected");
}
dealloc(nums[i]);
}
reset_temporary_storage();
push_allocator(temp);
int* foo = (int*)alloc(72);
*foo = 1337;
void* bar = alloc(69);
void* baz = alloc(420);
assert(*foo == 1337, "Temp memory corruptada");
int* old_foo = foo;
reset_temporary_storage();
foo = (int*)alloc(72);
assert(old_foo == foo, "Temp allocator goof");
pop_allocator();
// Repeated Allocation and Free
for (int i = 0; i < 10000; ++i) {
void* temp = alloc(128);
assert(temp != NULL && "Repeated allocation failed");
dealloc(temp);
}
// Mixed Size Allocations
void* mixed_blocks[200];
for (int i = 0; i < 200; ++i) {
if (i % 2 == 0) {
mixed_blocks[i] = alloc(128);
} else {
mixed_blocks[i] = alloc(1024 * 1024); // 1MB blocks
}
assert(mixed_blocks[i] != NULL && "Mixed size allocation failed");
}
for (int i = 0; i < 200; ++i) {
if (i % 2 == 0) {
dealloc(mixed_blocks[i]);
}
}
for (int i = 0; i < 200; ++i) {
if (i % 2 != 0) {
dealloc(mixed_blocks[i]);
}
}
// Fragmentation Stress Test
for (int i = 0; i < 50; ++i) {
blocks[i] = alloc(256);
assert(blocks[i] != NULL && "Failed to allocate small block for fragmentation test");
}
for (int i = 0; i < 50; i += 2) {
dealloc(blocks[i]);
}
for (int i = 50; i < 100; ++i) {
blocks[i] = alloc(128);
assert(blocks[i] != NULL && "Failed to allocate small block in fragmented heap");
}
for (int i = 50; i < 100; ++i) {
dealloc(blocks[i]);
}
for (int i = 1; i < 50; i += 2) {
dealloc(blocks[i]);
}
if (do_log_heap) log_heap();
}
void test_thread_proc1(Thread* t) {
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
}
Mutex_Handle test_mutex;
void test_thread_proc2(Thread* t) {
os_lock_mutex(test_mutex);
printf("Thread %llu part 1\n", t->id);
os_sleep(1);
printf("Thread %llu part 2\n", t->id);
os_sleep(1);
printf("Thread %llu part 3\n", t->id);
os_unlock_mutex(test_mutex);
}
void test_threads() {
Thread* t = os_make_thread(test_thread_proc1);
os_start_thread(t);
os_sleep(10);
printf("This should be printed in middle of thread execution\n");
os_join_thread(t);
printf("Thread is joined\n");
Mutex_Handle m = os_make_mutex();
os_lock_mutex(m);
os_unlock_mutex(m);
test_mutex = os_make_mutex();
Thread *threads[100];
for (int i = 0; i < 100; i++) {
threads[i] = os_make_thread(test_thread_proc2);
os_start_thread(threads[i]);
}
for (int i = 0; i < 100; i++) {
os_join_thread(threads[i]);
}
}
void test_allocator_threaded(Thread *t) {
for (int i = 0; i < 1000; ++i) {
void* temp = alloc(128);
assert(temp != NULL && "Repeated allocation failed");
dealloc(temp);
}
void* mixed_blocks[40];
for (int i = 0; i < 40; ++i) {
if (i % 2 == 0) {
mixed_blocks[i] = alloc(128);
} else {
mixed_blocks[i] = alloc(1024 * 1024); // 1MB blocks
}
assert(mixed_blocks[i] != NULL && "Mixed size allocation failed");
}
for (int i = 0; i < 40; ++i) {
if (i % 2 == 0) {
dealloc(mixed_blocks[i]);
}
}
for (int i = 0; i < 40; ++i) {
if (i % 2 != 0) {
dealloc(mixed_blocks[i]);
}
}
}
void test_strings() {
// Test length_of_null_terminated_string
assert(length_of_null_terminated_string("Test") == 4, "Failed: length_of_null_terminated_string");
assert(length_of_null_terminated_string("") == 0, "Failed: length_of_null_terminated_string");
// Test alloc_string and dealloc_string
string alloc_str = alloc_string(10);
assert(alloc_str.data != NULL, "Failed: alloc_string");
assert(alloc_str.count == 10, "Failed: alloc_string");
dealloc_string(alloc_str);
// Test string_concat
string str1 = const_string("Hello, ");
string str2 = const_string("World!");
string concat_str = string_concat(str1, str2);
assert(concat_str.count == str1.count + str2.count, "Failed: string_concat");
assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat");
dealloc_string(concat_str);
// Test convert_to_null_terminated_string
char* cstr = convert_to_null_terminated_string(str1);
assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string");
dealloc(cstr);
// Test temp_convert_to_null_terminated_string
cstr = temp_convert_to_null_terminated_string(str2);
assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string");
// No need to dealloc, it's temporary storage
// Test sprint
string format_str = const_string("Number: %d");
string formatted_str = sprint(format_str, 42);
char* formatted_cstr = convert_to_null_terminated_string(formatted_str);
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
dealloc(formatted_str.data);
dealloc(formatted_cstr);
// Test tprint
string temp_formatted_str = tprint(format_str, 100);
formatted_cstr = temp_convert_to_null_terminated_string(temp_formatted_str);
assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint");
// No need to dealloc, it's temporary storage
// Test print and printf (visual inspection)
printf("Expected output: Hello, World!\n");
print(const_string("Hello, %s!\n"), const_string("World"));
printf("Expected output: Number: 1234\n");
print(const_string("Number: %d\n"), 1234);
printf("Expected output: Number: 1234\n");
print(const_string("Number: %d\n"), 1234);
printf("Expected output: Mixed values: 42 and 3.14\n");
print(const_string("Mixed values: %d and %.2f\n"), 42, 3.14);
// This should fail assert and print descriptive error
//printf("Expected output (printf): Hello, World!\n");
//printf("Hello, %cs!\n", 5);
// This should fail assert and print descriptive error
// printf("Expected output (printf): Hello, World!\n");
// printf("Hello, %s!\n", "World");
printf("Expected output (printf): Hello, World!\n");
printf("Hello, %s!\n", cstr("World"));
printf("Expected output (printf): Number: 5678\n");
printf("Number: %d\n", 5678);
printf("Expected output (printf): Mixed values: 99 and 2.71\n");
printf("Mixed values: %d and %.2f\n", 99, 2.71);
// Test handling of empty strings
string empty_str = const_string("");
string concat_empty_str = string_concat(empty_str, empty_str);
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
dealloc_string(concat_empty_str);
// Test very large strings (performance test)
string large_str1 = alloc_string(1024 * 1024);
string large_str2 = alloc_string(1024 * 1024);
string large_concat_str = string_concat(large_str1, large_str2);
assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat");
dealloc_string(large_str1);
dealloc_string(large_str2);
dealloc_string(large_concat_str);
// Test string with special characters
string special_char_str = const_string("Special chars: \n\t\r");
cstr = convert_to_null_terminated_string(special_char_str);
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
dealloc(cstr);
string a = tprintf("Hello, %cs!\n", "balls");
string balls1 = string_view(a, 7, 5);
string balls2 = const_string("balls");
assert(strings_match(balls1, balls2), "String match failed");
assert(!strings_match(balls1, a), "String match failed");
}
void oogabooga_run_tests() {
printf("Testing allocator...\n");
test_allocator(true);
printf("OK!\n");
printf("Testing threads...\n");
test_threads();
printf("OK!\n");
printf("Testing strings...\n");
test_strings();
printf("OK!\n");
printf("Thread bombing allocator...\n");
Thread* threads[100];
for (int i = 0; i < 100; i++) {
threads[i] = os_make_thread(test_allocator_threaded);
os_start_thread(threads[i]);
}
for (int i = 0; i < 100; i++) {
os_join_thread(threads[i]);
}
printf("OK!\n");
}