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

1422 lines
53 KiB
C
Raw Normal View History

///
/// Most of these are generated by gpt so there might be some goofyness
///
void log_heap() {
spinlock_acquire_or_wait(&heap_lock);
print("\nHEAP:\n");
Heap_Block *block = heap_head;
while (block != 0) {
print("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size);
Heap_Free_Node *node = block->free_head;
u64 total_free = 0;
while (node != 0) {
print("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size);
total_free += node->size;
node = node->next;
}
print("\t TOTAL FREE: %llu\n\n", total_free);
block = block->next;
}
spinlock_release(&heap_lock);
}
void test_allocator(bool do_log_heap) {
2024-07-01 13:10:06 +02:00
2024-07-06 15:50:16 +02:00
u64 h = get_hash((string*)69);
2024-07-01 13:10:06 +02:00
Allocator heap = get_heap_allocator();
// Basic allocation and free
2024-07-01 13:10:06 +02:00
int* a = (int*)alloc(heap, sizeof(int));
int* b = (int*)alloc(heap, sizeof(int));
int* c = (int*)alloc(heap, 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
2024-07-02 19:12:31 +02:00
//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");
2024-07-02 19:12:31 +02:00
u8 check_bytes[1024];
for (u64 i = 0; i < 1024; i += 4) {
*((int*)&check_bytes[i]) = (int)get_random();
}
u8 *check_bytes_copy = (u8*)alloc(heap, 1024);
memcpy(check_bytes_copy, check_bytes, 1024);
// Allocate and free large block
2024-07-02 19:12:31 +02:00
//void* large_block = alloc(heap, 1024 * 1024 * 100);
//dealloc(heap, large_block);
// Allocate multiple small blocks
void* blocks[100];
for (int i = 0; i < 100; ++i) {
2024-07-01 13:10:06 +02:00
blocks[i] = alloc(heap, 128);
assert(blocks[i] != NULL, "Failed to allocate small block");
}
for (int i = 0; i < 100; ++i) {
2024-07-01 13:10:06 +02:00
dealloc(heap, blocks[i]);
}
// Stress test with various sizes
for (int i = 1; i <= 1000; ++i) {
2024-07-01 13:10:06 +02:00
void* p = alloc(heap, i * 64);
assert(p != NULL, "Failed to allocate varying size block");
2024-07-01 13:10:06 +02:00
dealloc(heap, p);
}
2024-07-02 19:12:31 +02:00
// Free in reverse order
for (int i = 0; i < 100; ++i) {
2024-07-01 13:10:06 +02:00
blocks[i] = alloc(heap, 128);
assert(blocks[i] != NULL, "Failed to allocate small block");
}
for (int i = 99; i >= 0; --i) {
2024-07-01 13:10:06 +02:00
dealloc(heap, blocks[i]);
}
// Test memory integrity with various allocation patterns
int* nums[10];
for (int i = 0; i < 10; ++i) {
2024-07-01 13:10:06 +02:00
nums[i] = (int*)alloc(heap, sizeof(int) * 10);
for (int j = 0; j < 10; ++j) {
nums[i][j] = i * 10 + j;
}
}
2024-07-02 19:12:31 +02:00
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 10; ++j) {
assert(nums[i][j] == i * 10 + j, "Memory corruption detected");
}
2024-07-01 13:10:06 +02:00
dealloc(heap, nums[i]);
}
2024-07-02 19:12:31 +02:00
reset_temporary_storage();
2024-07-02 19:12:31 +02:00
int* foo = (int*)alloc(get_temporary_allocator(), 72);
*foo = 1337;
void* bar = alloc(get_temporary_allocator(), 69);
(void)bar;
void* baz = alloc(get_temporary_allocator(), 420);
(void)baz;
assert(*foo == 1337, "Temp memory corruptada");
int* old_foo = foo;
reset_temporary_storage();
foo = (int*)alloc(get_temporary_allocator(), 72);
assert(old_foo == foo, "Temp allocator goof");
// Repeated Allocation and Free
for (int i = 0; i < 10000; ++i) {
2024-07-01 13:10:06 +02:00
void* temp = alloc(heap, 128);
assert(temp != NULL && "Repeated allocation failed");
2024-07-01 13:10:06 +02:00
dealloc(heap, temp);
}
// Mixed Size Allocations
void* mixed_blocks[200];
for (int i = 0; i < 200; ++i) {
if (i % 2 == 0) {
2024-07-01 13:10:06 +02:00
mixed_blocks[i] = alloc(heap, 128);
} else {
2024-07-01 13:10:06 +02:00
mixed_blocks[i] = alloc(heap, 1024 * 1024); // 1MB blocks
}
assert(mixed_blocks[i] != NULL && "Mixed size allocation failed");
}
for (int i = 0; i < 200; ++i) {
if (i % 2 == 0) {
2024-07-01 13:10:06 +02:00
dealloc(heap, mixed_blocks[i]);
}
}
for (int i = 0; i < 200; ++i) {
if (i % 2 != 0) {
2024-07-01 13:10:06 +02:00
dealloc(heap, mixed_blocks[i]);
}
}
// Fragmentation Stress Test
for (int i = 0; i < 50; ++i) {
2024-07-01 13:10:06 +02:00
blocks[i] = alloc(heap, 256);
assert(blocks[i] != NULL && "Failed to allocate small block for fragmentation test");
}
for (int i = 0; i < 50; i += 2) {
2024-07-01 13:10:06 +02:00
dealloc(heap, blocks[i]);
}
for (int i = 50; i < 100; ++i) {
2024-07-01 13:10:06 +02:00
blocks[i] = alloc(heap, 128);
assert(blocks[i] != NULL && "Failed to allocate small block in fragmented heap");
}
for (int i = 50; i < 100; ++i) {
2024-07-01 13:10:06 +02:00
dealloc(heap, blocks[i]);
}
for (int i = 1; i < 50; i += 2) {
2024-07-01 13:10:06 +02:00
dealloc(heap, blocks[i]);
}
2024-07-02 19:12:31 +02:00
assert(bytes_match(check_bytes, check_bytes_copy, 1024), "Memory corrupt");
if (do_log_heap) log_heap();
}
void test_thread_proc1(Thread* t) {
os_sleep(5);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
print("Hello from thread %llu\n", t->id);
}
void test_threads() {
2024-07-01 13:10:06 +02:00
2024-07-17 16:36:13 +02:00
Thread t;
os_thread_init(&t, test_thread_proc1);
os_thread_start(&t);
os_sleep(20);
print("This should be printed in middle of thread execution\n");
2024-07-17 16:36:13 +02:00
os_thread_join(&t);
print("Thread is joined\n");
Mutex_Handle m = os_make_mutex();
os_lock_mutex(m);
os_unlock_mutex(m);
}
void test_allocator_threaded(Thread *t) {
2024-07-01 13:10:06 +02:00
Allocator heap = get_heap_allocator();
for (int i = 0; i < 1000; ++i) {
2024-07-01 13:10:06 +02:00
void* temp = alloc(heap, 128);
assert(temp != NULL && "Repeated allocation failed");
2024-07-01 13:10:06 +02:00
dealloc(heap, temp);
}
void* mixed_blocks[40];
for (int i = 0; i < 40; ++i) {
if (i % 2 == 0) {
2024-07-01 13:10:06 +02:00
mixed_blocks[i] = alloc(heap, 128);
} else {
2024-07-01 13:10:06 +02:00
mixed_blocks[i] = alloc(heap, 1024 * 1024); // 1MB blocks
}
assert(mixed_blocks[i] != NULL && "Mixed size allocation failed");
}
for (int i = 0; i < 40; ++i) {
if (i % 2 == 0) {
2024-07-01 13:10:06 +02:00
dealloc(heap, mixed_blocks[i]);
}
}
for (int i = 0; i < 40; ++i) {
if (i % 2 != 0) {
2024-07-01 13:10:06 +02:00
dealloc(heap, mixed_blocks[i]);
}
}
}
void test_strings() {
2024-07-01 13:10:06 +02:00
Allocator heap = get_heap_allocator();
{
// 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(heap, 10);
assert(alloc_str.data != NULL, "Failed: alloc_string");
assert(alloc_str.count == 10, "Failed: alloc_string");
dealloc_string(heap, alloc_str);
// Test string_concat
string str1 = STR("Hello, ");
string str2 = STR("World!");
string concat_str = string_concat(str1, str2, heap);
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(heap, concat_str);
// Test convert_to_null_terminated_string
char* cstr = convert_to_null_terminated_string(str1, heap);
assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string");
dealloc(heap, 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");
// Test sprint
string format_str = STR("Number: %d");
string formatted_str = sprint(heap, format_str, 42);
char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap);
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
dealloc(heap, formatted_str.data);
dealloc(heap, 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");
// Test print and printf (visual inspection)
print("Expected output: Hello, World!\n");
print("Hello, %s!\n", STR("World"));
print("Expected output: Number: 1234\n");
print("Number: %d\n", 1234);
print("Expected output: Number: 1234\n");
print("Number: %d\n", 1234);
print("Expected output: Mixed values: 42 and 3.14\n");
print("Mixed values: %d and %.2f\n", 42, 3.14);
// This should fail assert and print descriptive error
//print("Expected output (printf): Hello, World!\n");
//print("Hello, %cs!\n", 5);
// This should fail assert and print descriptive error
// print("Expected output (printf): Hello, World!\n");
// print("Hello, %s!\n", "World");
print("Expected output (printf): Hello, World!\n");
print("Hello, %s!\n", STR("World"));
print("Expected output (printf): Number: 5678\n");
print("Number: %d\n", 5678);
print("Expected output (printf): Mixed values: 99 and 2.71\n");
print("Mixed values: %d and %.2f\n", 99, 2.71);
// Test handling of empty strings
string empty_str = STR("");
string concat_empty_str = string_concat(empty_str, empty_str, heap);
2024-07-15 21:40:27 +02:00
assert(concat_empty_str.count == 0 && !concat_empty_str.data, "Failed: string_concat with empty strings");
// Test very large strings (performance test)
string large_str1 = alloc_string(heap, 1024 * 1024);
string large_str2 = alloc_string(heap, 1024 * 1024);
string large_concat_str = string_concat(large_str1, large_str2, heap);
assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat");
dealloc_string(heap, large_str1);
dealloc_string(heap, large_str2);
dealloc_string(heap, large_concat_str);
// Test string with special characters
string special_char_str = STR("Special chars: \n\t\r");
cstr = convert_to_null_terminated_string(special_char_str, heap);
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
dealloc(heap, cstr);
string a = tprint("Hello, %cs!\n", "balls");
string balls1 = string_view(a, 7, 5);
string balls2 = STR("balls");
assert(strings_match(balls1, balls2), "String match failed");
assert(!strings_match(balls1, a), "String match failed");
}
// Test string_builder_init
String_Builder builder;
string_builder_init(&builder, heap);
assert(builder.buffer != NULL, "Failed: string_builder_init");
assert(builder.buffer_capacity >= 128, "Failed: string_builder_init");
assert(builder.count == 0, "Failed: string_builder_init");
// Test string_builder_reserve
string_builder_reserve(&builder, 256);
assert(builder.buffer_capacity >= 256, "Failed: string_builder_reserve");
// Test string_builder_append
string str1 = STR("Hello, ");
string_builder_append(&builder, str1);
assert(builder.count == str1.count, "Failed: string_builder_append");
assert(memcmp(builder.buffer, str1.data, str1.count) == 0, "Failed: string_builder_append");
string str2 = STR("World!");
string_builder_append(&builder, str2);
assert(builder.count == str1.count + str2.count, "Failed: string_builder_append");
assert(memcmp(builder.buffer, "Hello, World!", builder.count) == 0, "Failed: string_builder_append");
// Test string_builder_prints
string format_str = STR(" Number: %d");
string_builder_prints(&builder, format_str, 42);
char* expected_result = "Hello, World! Number: 42";
assert(builder.count == strlen(expected_result), "Failed: string_builder_prints");
assert(memcmp(builder.buffer, expected_result, builder.count) == 0, "Failed: string_builder_prints");
// Test string_builder_printf
string_builder_printf(&builder, " And a float: %.2f", 3.14);
expected_result = "Hello, World! Number: 42 And a float: 3.14";
assert(builder.count == strlen(expected_result), "Failed: string_builder_printf");
assert(memcmp(builder.buffer, expected_result, builder.count) == 0, "Failed: string_builder_printf");
// Test string_builder_get_string
2024-07-19 16:45:58 +02:00
string result_str = string_builder_get_string(builder);
assert(result_str.count == builder.count, "Failed: string_builder_get_string");
assert(memcmp(result_str.data, builder.buffer, result_str.count) == 0, "Failed: string_builder_get_string");
// Cleanup
dealloc(heap, builder.buffer);
// Test handling of empty builder
String_Builder empty_builder;
string_builder_init(&empty_builder, heap);
2024-07-19 16:45:58 +02:00
result_str = string_builder_get_string(empty_builder);
assert(result_str.count == 0, "Failed: empty builder handling");
dealloc(heap, empty_builder.buffer);
// Test appending large strings (performance test)
String_Builder large_builder;
string_builder_init(&large_builder, heap);
string large_str = alloc_string(heap, 1024 * 1024);
memset(large_str.data, 'A', 1024 * 1024);
string_builder_append(&large_builder, large_str);
assert(large_builder.count == 1024 * 1024, "Failed: large string_builder_append");
dealloc_string(heap, large_str);
dealloc(heap, large_builder.buffer);
// Test appending special characters
String_Builder special_char_builder;
string_builder_init(&special_char_builder, heap);
string special_char_str = STR("Special chars: \n\t\r");
string_builder_append(&special_char_builder, special_char_str);
assert(special_char_builder.count == special_char_str.count, "Failed: special character append");
assert(memcmp(special_char_builder.buffer, special_char_str.data, special_char_str.count) == 0, "Failed: special character append");
dealloc(heap, special_char_builder.buffer);
// Test multiple appends
String_Builder multi_append_builder;
string_builder_init(&multi_append_builder, heap);
string str_a = STR("First part");
string str_b = STR(" and second part");
string str_c = STR(" and third part.");
string_builder_append(&multi_append_builder, str_a);
string_builder_append(&multi_append_builder, str_b);
string_builder_append(&multi_append_builder, str_c);
expected_result = "First part and second part and third part.";
assert(multi_append_builder.count == strlen(expected_result), "Failed: multiple appends");
assert(memcmp(multi_append_builder.buffer, expected_result, multi_append_builder.count) == 0, "Failed: multiple appends");
dealloc(heap, multi_append_builder.buffer);
2024-07-19 16:45:58 +02:00
string cheese_hello = STR("HeCHEESElloCHEESE, WorCHEESEld!");
string hello = string_replace_all(cheese_hello, STR("CHEESE"), STR(""), heap);
assert(strings_match(hello, STR("Hello, World!")), "Failed: string_replace");
string hello_balls = string_replace_all(hello, STR("Hello"), STR("Greetings"), heap);
hello_balls = string_replace_all(hello_balls, STR("World"), STR("Balls"), heap);
assert(strings_match(hello_balls, STR("Greetings, Balls!")), "Failed: string_replace");
}
void test_file_io() {
#if TARGET_OS == WINDOWS && !OOGABOOGA_LINK_EXTERNAL_INSTANCE
// Test win32_fixed_utf8_to_null_terminated_wide
string utf8_str = STR("Test");
2024-07-01 13:10:06 +02:00
u16 *wide_str = win32_fixed_utf8_to_null_terminated_wide(utf8_str, get_heap_allocator());
assert(wide_str != NULL, "Failed: win32_fixed_utf8_to_null_terminated_wide");
assert(wide_str[4] == 0, "Failed: win32_fixed_utf8_to_null_terminated_wide");
2024-07-01 13:10:06 +02:00
dealloc(get_heap_allocator(), wide_str);
// Test temp_win32_fixed_utf8_to_null_terminated_wide
wide_str = temp_win32_fixed_utf8_to_null_terminated_wide(utf8_str);
assert(wide_str != NULL, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide");
assert(wide_str[4] == 0, "Failed: temp_win32_fixed_utf8_to_null_terminated_wide");
#endif
File file = OS_INVALID_FILE;
os_file_close(file);
// Test os_file_open and os_file_close
file = os_file_open("test.txt", O_WRITE | O_CREATE);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
os_file_close(file);
// Test os_file_write_string and os_file_read
string hello_world_write = STR("Hello, World!");
file = os_file_open("test.txt", O_WRITE | O_CREATE);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
bool write_result = os_file_write_string(file, hello_world_write);
assert(write_result, "Failed: os_file_write_string");
os_file_close(file);
file = os_file_open("test.txt", O_READ);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (read)");
string hello_world_read = talloc_string(hello_world_write.count);
bool read_result = os_file_read(file, hello_world_read.data, hello_world_read.count, &hello_world_read.count);
assert(read_result, "Failed: os_file_read %d", GetLastError());
assert(strings_match(hello_world_read, hello_world_write), "Failed: os_file_read write/read mismatch");
os_file_close(file);
// Test os_file_write_bytes
file = os_file_open("test_bytes.txt", O_WRITE | O_CREATE);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
int int_data = 42;
write_result = os_file_write_bytes(file, &int_data, sizeof(int));
assert(write_result, "Failed: os_file_write_bytes");
os_file_close(file);
// Test os_read_entire_file and os_write_entire_file
string write_data = STR("Entire file test");
bool write_entire_result = os_write_entire_file("entire_test.txt", write_data);
assert(write_entire_result, "Failed: os_write_entire_file");
2024-07-01 13:10:06 +02:00
Allocator heap = get_heap_allocator();
string read_data;
bool read_entire_result = os_read_entire_file("entire_test.txt", &read_data, heap);
assert(read_entire_result, "Failed: os_read_entire_file");
assert(strings_match(read_data, write_data), "Failed: os_read_entire_file write/read mismatch");
assert(memcmp(read_data.data, write_data.data, write_data.count) == 0, "Failed: os_read_entire_file (content mismatch)");
2024-07-01 13:10:06 +02:00
dealloc(heap, read_data.data);
// Test fprint
File balls = os_file_open("balls.txt", O_WRITE | O_CREATE);
assert(balls != OS_INVALID_FILE, "Failed: Could not create balls.txt");
fprint(balls, "Hello, %cs!", "Balls");
os_file_close(balls);
string hello_balls;
read_entire_result = os_read_entire_file("balls.txt", &hello_balls, heap);
assert(read_entire_result, "Failed: could not read balls.txt");
assert(strings_match(hello_balls, STR("Hello, Balls!")), "Failed: balls read/write mismatch. Expected 'Hello, Balls!', got '%s'", hello_balls);
u64 integers[4096];
for (u64 i = 0; i < 4096; i++) {
integers[i] = get_random();
}
string integers_data;
integers_data.data = (u8*)integers;
integers_data.count = 4096*sizeof(u64);
bool ok = os_write_entire_file("integers", integers_data);
assert(ok, "write integers fail");
string integers_read;
ok = os_read_entire_file("integers", &integers_read, heap);
assert(ok, "read integers fail");
u64 *new_integers = (u64*)integers_data.data;
assert(integers_read.count == integers_data.count, "Failed: big file read/write mismatch. Read was %d and written was %d", integers_read.count, integers_data.count);
assert(strings_match(integers_data, integers_read), "Failed: big file read/write mismatch");
2024-07-03 23:01:46 +02:00
assert(os_is_file("test.txt"), "Failed: test.txt not recognized as file");
assert(os_is_file("test_bytes.txt"), "Failed: test_bytes.txt not recognized as file");
assert(os_is_file("entire_test.txt"), "Failed: entire_test.txt not recognized as file");
assert(os_is_file("balls.txt"), "Failed: balls.txt not recognized as file");
assert(os_is_file("integers"), "Failed: integers not recognized as file");
bool dir_ok = os_make_directory("test_dir", false);
assert(dir_ok, "Failed: os_make_directory");
assert(os_is_directory("test_dir"), "Failed: os_is_directory");
string dir_abs;
bool abs_ok = os_get_absolute_path(STR("test_dir"), &dir_abs, heap);
assert(abs_ok, "Failed: os_get_absolute_path");
assert(os_is_path_absolute(dir_abs), "Failed: os_is_path_absolute");
assert(!os_is_path_absolute(STR("test_dir")), "Failed: os_is_path_absolute");
string dir_rel;
bool rel_ok = os_get_relative_path(STR("."), dir_abs, &dir_rel, heap);
assert(rel_ok, "Failed: os_get_relative_path");
assert(os_do_paths_match(dir_rel, STR("test_dir")), "Failed: Resolved relative path does not match. Expected '.\test_dir', got %s.", dir_rel);
dir_ok = os_make_directory("test_dir1/test_dir2/test_dir3", true);
assert(dir_ok, "Failed: os_make_directory");
dir_ok = os_make_directory("test_dir1/test_dir2/test_dir4", true);
assert(dir_ok, "Failed: os_make_directory");
assert(os_is_directory("test_dir1"), "Failed: os_is_directory");
assert(os_is_directory("test_dir1/test_dir2"), "Failed: os_is_directory");
assert(os_is_directory("test_dir1/test_dir2//test_dir3"), "Failed: os_is_directory");
assert(os_is_directory("test_dir1/test_dir2//test_dir4"), "Failed: os_is_directory");
// Clean up test files
bool delete_ok = false;
delete_ok = os_file_delete("test.txt");
assert(delete_ok, "Failed: could not delete test.txt");
delete_ok = os_file_delete("test_bytes.txt");
assert(delete_ok, "Failed: could not delete test_bytes.txt");
delete_ok = os_file_delete("entire_test.txt");
assert(delete_ok, "Failed: could not delete entire_test.txt");
delete_ok = os_file_delete("balls.txt");
assert(delete_ok, "Failed: could not delete balls.txt");
delete_ok = os_file_delete("integers");
assert(delete_ok, "Failed: could not delete integers");
2024-07-03 23:01:46 +02:00
delete_ok = os_delete_directory("test_dir", false);
assert(delete_ok, "Failed: could not delete test_dir");
delete_ok = os_delete_directory("test_dir1", true);
assert(delete_ok, "Failed: could not delete test_dir1 (recursive)");
}
bool floats_roughly_match(float a, float b) {
return fabs(a - b) < 0.01;
}
void test_simd() {
f32 *a_f32 = alloc(get_heap_allocator(), 128*sizeof(f32));
f32 *b_f32 = alloc(get_heap_allocator(), 128*sizeof(f32));
f32 *result_f32 = alloc(get_heap_allocator(), 128*sizeof(f32));
s32 *a_i32 = alloc(get_heap_allocator(), 128*sizeof(f32));
s32 *b_i32 = alloc(get_heap_allocator(), 128*sizeof(f32));
s32 *result_i32 = alloc(get_heap_allocator(), 128*sizeof(f32));
(*(u64*)&a_f32) = ((*(u64*)&a_f32)+64) & ~(63);
(*(u64*)&b_f32) = ((*(u64*)&b_f32)+64) & ~(63);
(*(u64*)&result_f32) = ((*(u64*)&result_f32)+64) & ~(63);
(*(u64*)&a_i32) = ((*(u64*)&a_i32)+64) & ~(63);
(*(u64*)&b_i32) = ((*(u64*)&b_i32)+64) & ~(63);
(*(u64*)&result_i32) = ((*(u64*)&result_i32)+64) & ~(63);
assert((u64)a_f32%16 == 0);
assert((u64)b_f32%16 == 0);
assert((u64)result_f32%16 == 0);
assert((u64)a_i32%16 == 0);
assert((u64)b_i32%16 == 0);
assert((u64)result_i32%16 == 0);
assert((u64)a_f32%32 == 0);
assert((u64)b_f32%32 == 0);
assert((u64)result_f32%32 == 0);
assert((u64)a_i32%32 == 0);
assert((u64)b_i32%32 == 0);
assert((u64)result_i32%32 == 0);
assert((u64)a_f32%64 == 0);
assert((u64)b_f32%64 == 0);
assert((u64)result_f32%64 == 0);
assert((u64)a_i32%64 == 0);
assert((u64)b_i32%64 == 0);
assert((u64)result_i32%64 == 0);
for (int i = 0; i < 16; ++i) {
a_f32[i] = i * 1.0f;
b_f32[i] = (i + 1) * 2.0f;
a_i32[i] = i;
b_i32[i] = i + 1;
}
// Test function pointers setup
query_cpu_capabilities();
// Test float32 add
simd_add_float32_64(a_f32, b_f32, result_f32);
assert(floats_roughly_match(result_f32[0], a_f32[0]+b_f32[0]), "SIMD add float32 64 failed");
2024-07-04 12:18:16 +02:00
simd_add_float32_128_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 4; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] + b_f32[i]), "SIMD add float32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_add_float32_256_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 8; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] + b_f32[i]), "SIMD add float32 256 failed");
}
2024-07-04 12:18:16 +02:00
simd_add_float32_512_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 16; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] + b_f32[i]), "SIMD add float32 512 failed");
}
// Test float32 subtract
simd_sub_float32_64(a_f32, b_f32, result_f32);
assert(floats_roughly_match(result_f32[0], a_f32[0] - b_f32[0]), "SIMD sub float32 64 failed");
2024-07-04 12:18:16 +02:00
simd_sub_float32_128_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 4; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] - b_f32[i]), "SIMD sub float32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_sub_float32_256_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 8; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] - b_f32[i]), "SIMD sub float32 256 failed");
}
2024-07-04 12:18:16 +02:00
simd_sub_float32_512_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 16; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] - b_f32[i]), "SIMD sub float32 512 failed");
}
// Test float32 multiply
simd_mul_float32_64(a_f32, b_f32, result_f32);
assert(floats_roughly_match(result_f32[0], a_f32[0]*b_f32[0]), "SIMD mul float32 64 failed");
2024-07-04 12:18:16 +02:00
simd_mul_float32_128_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 4; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] * b_f32[i]), "SIMD mul float32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_mul_float32_256_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 8; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] * b_f32[i]), "SIMD mul float32 256 failed");
}
2024-07-04 12:18:16 +02:00
simd_mul_float32_512_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 16; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] * b_f32[i]), "SIMD mul float32 512 failed");
}
// Test float32 divide
simd_div_float32_64(a_f32, b_f32, result_f32);
assert(floats_roughly_match(result_f32[0], a_f32[0]/b_f32[0]), "SIMD div float32 64 failed");
2024-07-04 12:18:16 +02:00
simd_div_float32_128_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 4; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] / b_f32[i]), "SIMD div float32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_div_float32_256_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 8; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] / b_f32[i]), "SIMD div float32 256 failed, %.5f, %.5f, %.5f", result_f32[i], a_f32[i], a_f32[i] / b_f32[i]);
}
2024-07-04 12:18:16 +02:00
simd_div_float32_512_aligned(a_f32, b_f32, result_f32);
for (int i = 0; i < 16; ++i) {
assert(floats_roughly_match(result_f32[i], a_f32[i] / b_f32[i]), "SIMD div float32 512 failed");
}
// Test int32 add
2024-07-04 12:18:16 +02:00
simd_add_int32_128_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 4; ++i) {
assert(result_i32[i] == a_i32[i] + b_i32[i], "SIMD add int32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_add_int32_256_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 8; ++i) {
assert(result_i32[i] == a_i32[i] + b_i32[i], "SIMD add int32 256 failed");
}
2024-07-04 12:18:16 +02:00
simd_add_int32_512_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 16; ++i) {
assert(result_i32[i] == a_i32[i] + b_i32[i], "SIMD add int32 512 failed");
}
// Test int32 subtract
2024-07-04 12:18:16 +02:00
simd_sub_int32_128_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 4; ++i) {
assert(result_i32[i] == a_i32[i] - b_i32[i], "SIMD sub int32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_sub_int32_256_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 8; ++i) {
assert(result_i32[i] == a_i32[i] - b_i32[i], "SIMD sub int32 256 failed");
}
2024-07-04 12:18:16 +02:00
simd_sub_int32_512_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 16; ++i) {
assert(result_i32[i] == a_i32[i] - b_i32[i], "SIMD sub int32 512 failed");
}
// Test int32 multiply
2024-07-04 12:18:16 +02:00
simd_mul_int32_128_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 4; ++i) {
assert(result_i32[i] == a_i32[i] * b_i32[i], "SIMD mul int32 128 failed");
}
2024-07-04 12:18:16 +02:00
simd_mul_int32_256_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 8; ++i) {
assert(result_i32[i] == a_i32[i] * b_i32[i], "SIMD mul int32 256 failed");
}
2024-07-04 12:18:16 +02:00
simd_mul_int32_512_aligned(a_i32, b_i32, result_i32);
for (int i = 0; i < 16; ++i) {
assert(result_i32[i] == a_i32[i] * b_i32[i], "SIMD mul int32 512 failed");
}
#define _TEST_NUM_SAMPLES ((100000 + 64) & ~(63))
assert(_TEST_NUM_SAMPLES % 16 == 0);
2024-07-04 12:18:16 +02:00
float *samples_a = alloc(get_heap_allocator(), _TEST_NUM_SAMPLES*sizeof(float)+512);
float *samples_b = alloc(get_heap_allocator(), _TEST_NUM_SAMPLES*sizeof(float)+512);
samples_a = (float*)(((u64)samples_a+64)&~(63));
samples_b = (float*)(((u64)samples_b+64)&~(63));
memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float));
memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float));
2024-07-17 16:36:13 +02:00
u64 start = rdtsc();
for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 16) {
2024-07-04 12:18:16 +02:00
simd_mul_float32_512_aligned(&samples_a[i], &samples_b[i], &samples_a[i]);
}
2024-07-17 16:36:13 +02:00
u64 end = rdtsc();
u64 cycles = end-start;
print("simd 512 float32 mul took %llu cycles\n", cycles);
memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float));
memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float));
2024-07-17 16:36:13 +02:00
start = rdtsc();
for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 8) {
2024-07-04 12:18:16 +02:00
simd_mul_float32_256_aligned(&samples_a[i], &samples_b[i], &samples_a[i]);
}
2024-07-17 16:36:13 +02:00
end = rdtsc();
cycles = end-start;
print("simd 256 float32 mul took %llu cycles\n", cycles);
memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float));
memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float));
2024-07-17 16:36:13 +02:00
start = rdtsc();
for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 4) {
2024-07-04 12:18:16 +02:00
simd_mul_float32_128_aligned(&samples_a[i], &samples_b[i], &samples_a[i]);
}
2024-07-17 16:36:13 +02:00
end = rdtsc();
cycles = end-start;
print("simd 128 float32 mul took %llu cycles\n", cycles);
memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float));
memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float));
2024-07-17 16:36:13 +02:00
start = rdtsc();
for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 2) {
simd_mul_float32_64(&samples_a[i], &samples_b[i], &samples_a[i]);
}
2024-07-17 16:36:13 +02:00
end = rdtsc();
cycles = end-start;
print("simd 64 float32 mul took %llu cycles\n", cycles);
memset(samples_a, 2, _TEST_NUM_SAMPLES*sizeof(float));
memset(samples_b, 2, _TEST_NUM_SAMPLES*sizeof(float));
2024-07-17 16:36:13 +02:00
start = rdtsc();
for (u64 i = 0; i < _TEST_NUM_SAMPLES; i += 1) {
samples_a[i] = samples_a[i] + samples_b[i];
}
2024-07-17 16:36:13 +02:00
end = rdtsc();
cycles = end-start;
print("NO SIMD float32 mul took %llu cycles\n", cycles);
}
2024-07-04 12:18:16 +02:00
// Indirect testing of some simd stuff
void test_linmath() {
// Test vector creation and access
Vector2 v2_test = v2(1.0f, 2.0f);
assert(v2_test.x == 1.0f && v2_test.y == 2.0f, "v2 creation incorrect");
Vector3 v3_test = v3(1.0f, 2.0f, 3.0f);
assert(v3_test.x == 1.0f && v3_test.y == 2.0f && v3_test.z == 3.0f, "v3 creation incorrect");
Vector4 v4_test = v4(1.0f, 2.0f, 3.0f, 4.0f);
assert(v4_test.x == 1.0f && v4_test.y == 2.0f && v4_test.z == 3.0f && v4_test.w == 4.0f, "v4 creation incorrect");
// Test vector operations
Vector2 v2_a = v2(3.0f, 4.0f);
Vector2 v2_b = v2(1.0f, 2.0f);
Vector2 v2_result = v2_add(v2_a, v2_b);
assert(v2_result.x == 4.0f && v2_result.y == 6.0f, "v2_add incorrect. %.3f, %.3f", v2_result.x, v2_result.y);
v2_result = v2_sub(v2_a, v2_b);
assert(v2_result.x == 2.0f && v2_result.y == 2.0f, "v2_sub incorrect");
v2_result = v2_mul(v2_a, v2_b);
assert(v2_result.x == 3.0f && v2_result.y == 8.0f, "v2_mul incorrect");
v2_result = v2_div(v2_a, v2_b);
assert(v2_result.x == 3.0f && v2_result.y == 2.0f, "v2_div incorrect");
v2_result = v2_mulf(v2_a, 2.0f);
assert(v2_result.x == 6.0f && v2_result.y == 8.0f, "v2_mulf incorrect");
v2_result = v2_divf(v2_a, 2.0f);
assert(v2_result.x == 1.5f && v2_result.y == 2.0f, "v2_divf incorrect");
// Test matrix operations
Matrix4 m1 = m4_scalar(2.0f);
assert(m1.data[0] == 2.0f && m1.data[5] == 2.0f && m1.data[10] == 2.0f && m1.data[15] == 2.0f, "m4_scalar incorrect");
Vector3 translation = v3(1.0f, 2.0f, 3.0f);
Matrix4 m2 = m4_make_translation(translation);
assert(m2.m[0][3] == 1.0f && m2.m[1][3] == 2.0f && m2.m[2][3] == 3.0f, "m4_make_translation incorrect");
Vector3 scale = v3(2.0f, 3.0f, 4.0f);
Matrix4 m4 = m4_make_scale(scale);
assert(m4.m[0][0] == 2.0f && m4.m[1][1] == 3.0f && m4.m[2][2] == 4.0f, "m4_make_scale incorrect");
// Test orthographic projection matrix
Matrix4 ortho = m4_make_orthographic_projection(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f);
assert(ortho.m[0][0] == 1.0f && ortho.m[1][1] == 1.0f && ortho.m[2][2] == 1.0f, "m4_make_orthographic_projection incorrect");
// Test matrix multiplication
Matrix4 m5 = m4_scalar(1.0f);
m5.m[0][3] = 1.0f; m5.m[1][3] = 2.0f; m5.m[2][3] = 3.0f;
Matrix4 result_matrix = m4_mul(m2, m5);
assert(result_matrix.m[0][3] == 2.0f && result_matrix.m[1][3] == 4.0f && result_matrix.m[2][3] == 6.0f, "m4_mul incorrect");
// Test matrix inverse
Matrix4 identity = m4_scalar(1.0f);
Matrix4 inverse_matrix = m4_inverse(identity);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
assert(inverse_matrix.m[i][j] == identity.m[i][j], "m4_inverse incorrect for identity matrix");
}
}
// Test Vector2 creation
Vector2 v2_test1 = v2(1.0f, 2.0f);
assert(v2_test1.x == 1.0f && v2_test1.y == 2.0f, "Vector2 creation failed");
Vector2 v2_test2 = v2(3.0f, 4.0f);
assert(v2_test2.x == 3.0f && v2_test2.y == 4.0f, "Vector2 creation failed");
// Test Vector2 addition
Vector2 v2_add_result = v2_add(v2_test1, v2_test2);
assert(v2_add_result.x == 4.0f && v2_add_result.y == 6.0f, "Vector2 addition failed");
// Test Vector2 subtraction
Vector2 v2_sub_result = v2_sub(v2_test2, v2_test1);
assert(v2_sub_result.x == 2.0f && v2_sub_result.y == 2.0f, "Vector2 subtraction failed");
// Test Vector2 multiplication
Vector2 v2_mul_result = v2_mul(v2_test1, v2_test2);
assert(v2_mul_result.x == 3.0f && v2_mul_result.y == 8.0f, "Vector2 multiplication failed");
// Test Vector2 scalar multiplication
Vector2 v2_mulf_result = v2_mulf(v2_test1, 2.0f);
assert(v2_mulf_result.x == 2.0f && v2_mulf_result.y == 4.0f, "Vector2 scalar multiplication failed");
// Test Vector2 division
Vector2 v2_div_result = v2_div(v2_test2, v2_test1);
assert(v2_div_result.x == 3.0f && v2_div_result.y == 2.0f, "Vector2 division failed");
// Test Vector2 scalar division
Vector2 v2_divf_result = v2_divf(v2_test2, 2.0f);
assert(v2_divf_result.x == 1.5f && v2_divf_result.y == 2.0f, "Vector2 scalar division failed");
// Test Vector2 normalization
Vector2 v2_norm_result = v2_normalize(v2(3.0f, 4.0f));
assert(fabs(v2_norm_result.x - 0.6f) < 1e-6 && fabs(v2_norm_result.y - 0.8f) < 1e-6, "Vector2 normalization failed");
// Test Vector2 rotation
Vector2 pivot = v2(0.0f, 0.0f);
Vector2 point = v2(1.0f, 0.0f);
Vector2 v2_rot_result = v2_rotate_point_around_pivot(point, pivot, PI32 / 2.0f);
assert(fabs(v2_rot_result.x) < 1e-6 && fabs(v2_rot_result.y - 1.0f) < 1e-6, "Vector2 rotation around pivot failed");
// Test Vector3 creation
Vector3 v3_test1 = v3(1.0f, 2.0f, 3.0f);
assert(v3_test1.x == 1.0f && v3_test1.y == 2.0f && v3_test1.z == 3.0f, "Vector3 creation failed");
Vector3 v3_test2 = v3(4.0f, 5.0f, 6.0f);
assert(v3_test2.x == 4.0f && v3_test2.y == 5.0f && v3_test2.z == 6.0f, "Vector3 creation failed");
// Test Vector3 addition
Vector3 v3_add_result = v3_add(v3_test1, v3_test2);
assert(v3_add_result.x == 5.0f && v3_add_result.y == 7.0f && v3_add_result.z == 9.0f, "Vector3 addition failed");
// Test Vector3 subtraction
Vector3 v3_sub_result = v3_sub(v3_test2, v3_test1);
assert(v3_sub_result.x == 3.0f && v3_sub_result.y == 3.0f && v3_sub_result.z == 3.0f, "Vector3 subtraction failed");
// Test Vector3 multiplication
Vector3 v3_mul_result = v3_mul(v3_test1, v3_test2);
assert(v3_mul_result.x == 4.0f && v3_mul_result.y == 10.0f && v3_mul_result.z == 18.0f, "Vector3 multiplication failed");
// Test Vector3 scalar multiplication
Vector3 v3_mulf_result = v3_mulf(v3_test1, 2.0f);
assert(v3_mulf_result.x == 2.0f && v3_mulf_result.y == 4.0f && v3_mulf_result.z == 6.0f, "Vector3 scalar multiplication failed");
// Test Vector3 division
Vector3 v3_div_result = v3_div(v3_test2, v3_test1);
assert(v3_div_result.x == 4.0f && v3_div_result.y == 2.5f && v3_div_result.z == 2.0f, "Vector3 division failed");
// Test Vector3 scalar division
Vector3 v3_divf_result = v3_divf(v3_test2, 2.0f);
assert(v3_divf_result.x == 2.0f && v3_divf_result.y == 2.5f && v3_divf_result.z == 3.0f, "Vector3 scalar division failed");
// Test Vector4 creation
Vector4 v4_test1 = v4(1.0f, 2.0f, 3.0f, 4.0f);
assert(v4_test1.x == 1.0f && v4_test1.y == 2.0f && v4_test1.z == 3.0f && v4_test1.w == 4.0f, "Vector4 creation failed");
Vector4 v4_test2 = v4(5.0f, 6.0f, 7.0f, 8.0f);
assert(v4_test2.x == 5.0f && v4_test2.y == 6.0f && v4_test2.z == 7.0f && v4_test2.w == 8.0f, "Vector4 creation failed");
// Test Vector4 addition
Vector4 v4_add_result = v4_add(v4_test1, v4_test2);
assert(v4_add_result.x == 6.0f && v4_add_result.y == 8.0f && v4_add_result.z == 10.0f && v4_add_result.w == 12.0f, "Vector4 addition failed");
// Test Vector4 subtraction
Vector4 v4_sub_result = v4_sub(v4_test2, v4_test1);
assert(v4_sub_result.x == 4.0f && v4_sub_result.y == 4.0f && v4_sub_result.z == 4.0f && v4_sub_result.w == 4.0f, "Vector4 subtraction failed");
// Test Vector4 multiplication
Vector4 v4_mul_result = v4_mul(v4_test1, v4_test2);
assert(v4_mul_result.x == 5.0f && v4_mul_result.y == 12.0f && v4_mul_result.z == 21.0f && v4_mul_result.w == 32.0f, "Vector4 multiplication failed");
// Test Vector4 scalar multiplication
Vector4 v4_mulf_result = v4_mulf(v4_test1, 2.0f);
assert(v4_mulf_result.x == 2.0f && v4_mulf_result.y == 4.0f && v4_mulf_result.z == 6.0f && v4_mulf_result.w == 8.0f, "Vector4 scalar multiplication failed");
// Test Vector4 division
Vector4 v4_div_result = v4_div(v4_test2, v4_test1);
assert(v4_div_result.x == 5.0f && v4_div_result.y == 3.0f && v4_div_result.w == 2.0f, "Vector4 division failed");
// Test Vector4 scalar division
Vector4 v4_divf_result = v4_divf(v4_test2, 2.0f);
assert(v4_divf_result.x == 2.5f && v4_divf_result.y == 3.0f && v4_divf_result.z == 3.5f && v4_divf_result.w == 4.0f, "Vector4 scalar division failed");
// Test mixed vector and scalar operations
Vector2 mixed_v2 = v2(2.0f, 4.0f);
Vector2 mixed_v2_result = v2_mulf(mixed_v2, 0.5f);
assert(mixed_v2_result.x == 1.0f && mixed_v2_result.y == 2.0f, "Mixed Vector2 scalar multiplication failed");
Vector3 mixed_v3 = v3(3.0f, 6.0f, 9.0f);
Vector3 mixed_v3_result = v3_divf(mixed_v3, 3.0f);
assert(mixed_v3_result.x == 1.0f && mixed_v3_result.y == 2.0f && mixed_v3_result.z == 3.0f, "Mixed Vector3 scalar division failed");
Vector4 mixed_v4 = v4(4.0f, 8.0f, 12.0f, 16.0f);
Vector4 mixed_v4_result = v4_mulf(mixed_v4, 0.25f);
assert(mixed_v4_result.x == 1.0f && mixed_v4_result.y == 2.0f && mixed_v4_result.z == 3.0f && mixed_v4_result.w == 4.0f, "Mixed Vector4 scalar multiplication failed");
2024-07-04 12:18:16 +02:00
2024-07-16 10:00:04 +02:00
float v2_dot_product = v2_dot(v2(2, 7), v2(3, 2));
float v3_dot_product = v3_dot(v3(2, 7, 2), v3(3, 2, 9));
float v4_dot_product = v4_dot(v4(2, 7, 6, 1), v4(3, 2, 1, 4));
2024-07-04 12:18:16 +02:00
2024-07-16 10:00:04 +02:00
assert(floats_roughly_match(v2_dot_product, 20), "Failed: v2_dot");
assert(floats_roughly_match(v3_dot_product, 38), "Failed: v3_dot");
assert(floats_roughly_match(v4_dot_product, 30), "Failed: v4_dot");
}
void test_intmath() {
// Test vector creation and access
Vector2i v2i_test = v2i(1, 2);
assert(v2i_test.x == 1 && v2i_test.y == 2, "v2i creation incorrect");
Vector3i v3i_test = v3i(1, 2, 3);
assert(v3i_test.x == 1 && v3i_test.y == 2 && v3i_test.z == 3, "v3i creation incorrect");
Vector4i v4i_test = v4i(1, 2, 3, 4);
assert(v4i_test.x == 1 && v4i_test.y == 2 && v4i_test.z == 3 && v4i_test.w == 4, "v4i creation incorrect");
// Test vector2 operations
Vector2i v2i_a = v2i(3, 4);
Vector2i v2i_b = v2i(1, 2);
Vector2i v2i_result = v2i_add(v2i_a, v2i_b);
assert(v2i_result.x == 4 && v2i_result.y == 6, "v2i_add incorrect");
v2i_result = v2i_sub(v2i_a, v2i_b);
assert(v2i_result.x == 2 && v2i_result.y == 2, "v2i_sub incorrect");
v2i_result = v2i_mul(v2i_a, v2i_b);
assert(v2i_result.x == 3 && v2i_result.y == 8, "v2i_mul incorrect");
v2i_result = v2i_div(v2i_a, v2i_b);
assert(v2i_result.x == 3 && v2i_result.y == 2, "v2i_div incorrect");
v2i_result = v2i_muli(v2i_a, 2);
assert(v2i_result.x == 6 && v2i_result.y == 8, "v2i_muli incorrect");
v2i_result = v2i_divi(v2i_a, 2);
assert(v2i_result.x == 1 && v2i_result.y == 2, "v2i_divi incorrect");
// Test vector2 operations
Vector3i v3i_a = v3i(3, 4, 6);
Vector3i v3i_b = v3i(1, 2, 3);
Vector3i v3i_result = v3i_add(v3i_a, v3i_b);
assert(v3i_result.x == 4 && v3i_result.y == 6 && v3i_result.z == 9, "v3i_add incorrect.");
v3i_result = v3i_sub(v3i_a, v3i_b);
assert(v3i_result.x == 2 && v3i_result.y == 2 && v3i_result.z == 3, "v3i_sub incorrect");
v3i_result = v3i_mul(v3i_a, v3i_b);
assert(v3i_result.x == 3 && v3i_result.y == 8 && v3i_result.z == 18, "v3i_mul incorrect");
v3i_result = v3i_div(v3i_a, v3i_b);
assert(v3i_result.x == 3 && v3i_result.y == 2 && v3i_result.z == 2, "v3i_div incorrect");
v3i_result = v3i_muli(v3i_a, 2);
assert(v3i_result.x == 6 && v3i_result.y == 8 && v3i_result.z == 12, "v3i_muli incorrect");
v3i_result = v3i_divi(v3i_a, 2);
assert(v3i_result.x == 1 && v3i_result.y == 2 && v3i_result.z == 3, "v3i_divi incorrect");
Vector4i v4i_a = v4i(3, 4, 6, 8);
Vector4i v4i_b = v4i(1, 2, 3, 4);
Vector4i v4i_result = v4i_add(v4i_a, v4i_b);
assert(v4i_result.x == 4 && v4i_result.y == 6 && v4i_result.z == 9 && v4i_result.w == 12, "v4i_add incorrect.");
v4i_result = v4i_sub(v4i_a, v4i_b);
assert(v4i_result.x == 2 && v4i_result.y == 2 && v4i_result.z == 3 && v4i_result.w == 4, "v4i_sub incorrect");
v4i_result = v4i_mul(v4i_a, v4i_b);
assert(v4i_result.x == 3 && v4i_result.y == 8 && v4i_result.z == 18 && v4i_result.w == 32, "v4i_mul incorrect");
v4i_result = v4i_div(v4i_a, v4i_b);
assert(v4i_result.x == 3 && v4i_result.y == 2 && v4i_result.z == 2 && v4i_result.w == 2, "v4i_div incorrect");
v4i_result = v4i_muli(v4i_a, 2);
assert(v4i_result.x == 6 && v4i_result.y == 8 && v4i_result.z == 12 && v4i_result.w == 16, "v4i_muli incorrect");
v4i_result = v4i_divi(v4i_a, 2);
assert(v4i_result.x == 1 && v4i_result.y == 2 && v4i_result.z == 3 && v4i_result.w == 4, "v4i_divi incorrect");
}
2024-07-06 15:50:16 +02:00
void test_hash_table() {
Hash_Table table = make_hash_table(string, int, get_heap_allocator());
string key1 = STR("Key string");
int value1 = 69;
bool newly_added = hash_table_set(&table, key1, value1);
assert(newly_added == true, "Failed: Key should be newly added");
int* found_value = hash_table_find(&table, key1);
assert(found_value != NULL, "Failed: Key should exist in hash table");
assert(*found_value == 69, "Failed: Value should be 69, got %i", *found_value);
int new_value1 = 70;
newly_added = hash_table_set(&table, key1, new_value1);
assert(newly_added == false, "Failed: Key should not be newly added");
found_value = hash_table_find(&table, key1);
assert(found_value != NULL, "Failed: Key should exist in hash table");
assert(*found_value == 70, "Failed: Value should be 70, got %i", *found_value);
bool contains = hash_table_contains(&table, key1);
assert(contains == true, "Failed: Hash table should contain key1");
string key2 = STR("Non-existing key");
contains = hash_table_contains(&table, key2);
assert(contains == false, "Failed: Hash table should not contain key2");
hash_table_reset(&table);
found_value = hash_table_find(&table, key1);
assert(found_value == NULL, "Failed: Hash table should be empty after reset");
hash_table_destroy(&table);
assert(table.entries == NULL, "Failed: Hash table entries should be NULL after destroy");
assert(table.count == 0, "Failed: Hash table count should be 0 after destroy");
assert(table.capacity_count == 0, "Failed: Hash table capacity count should be 0 after destroy");
}
#define NUM_BINS 100
#define NUM_SAMPLES 100000000
void test_random_distribution() {
int bins[NUM_BINS] = {0};
2024-07-17 16:36:13 +02:00
seed_for_random = rdtsc();
for (int i = 0; i < NUM_SAMPLES; i++) {
f32 rand_val = get_random_float32();
int bin = (int)(rand_val * NUM_BINS);
bins[bin]++;
}
int min_bin = INT32_MAX;
int max_bin = 0;
print("Histogram of Random Values:\n");
for (int i = 0; i < NUM_BINS; i++) {
print("Bin %d: %d\n", i, bins[i]);
min_bin = min(min_bin, bins[i]);
max_bin = max(max_bin, bins[i]);
}
print("Min: %d, max: %d\n", min_bin, max_bin);
}
#define MUTEX_TEST_TASK_COUNT 1000
typedef struct Mutex_Test_Shared_Data {
int counter;
bool any_active_thread;
Mutex mutex;
} Mutex_Test_Shared_Data;
void mutex_test_increment_counter(Thread* t) {
Mutex_Test_Shared_Data* data = (Mutex_Test_Shared_Data*)t->data;
for (int i = 0; i < MUTEX_TEST_TASK_COUNT; i++) {
mutex_acquire_or_wait(&data->mutex);
assert(!data->any_active_thread, "Failed: More than one thread is in critical section!");
data->any_active_thread = true;
data->counter++;
data->any_active_thread = false;
mutex_release(&data->mutex);
}
}
void test_mutex() {
Mutex m;
// Test initialization
mutex_init(&m);
assert(m.spin_time_microseconds == MUTEX_DEFAULT_SPIN_TIME_MICROSECONDS, "Failed: Default spin time incorrect");
assert(!m.spinlock_acquired, "Failed: Spinlock should not be acquired after initialization");
// Test acquire and release without contention
mutex_acquire_or_wait(&m);
assert(m.spinlock_acquired, "Failed: Mutex should be acquired after mutex_acquire_or_wait");
mutex_release(&m);
assert(!m.spinlock_acquired, "Failed: Spinlock should not be acquired after mutex_release");
// Clean up
mutex_destroy(&m);
Mutex_Test_Shared_Data data;
data.counter = 0;
data.any_active_thread = false;
mutex_init(&data.mutex);
Allocator allocator = get_heap_allocator();
const int num_threads = 100;
2024-07-17 16:36:13 +02:00
Thread *threads = alloc(allocator, sizeof(Thread)*num_threads);
for (u64 i = 0; i < num_threads; i++) {
2024-07-17 16:36:13 +02:00
os_thread_init(&threads[i], mutex_test_increment_counter);
threads[i].data = &data;
}
for (u64 i = 0; i < num_threads; i++) {
2024-07-17 16:36:13 +02:00
os_thread_start(&threads[i]);
}
for (u64 i = 0; i < num_threads; i++) {
2024-07-17 16:36:13 +02:00
os_thread_join(&threads[i]);
}
assert(data.counter == num_threads * MUTEX_TEST_TASK_COUNT, "Failed: Counter does not match expected value after threading tasks");
mutex_destroy(&data.mutex);
}
2024-07-22 01:06:21 +02:00
#ifndef OOGABOOGA_HEADLESS
int compare_draw_quads(const void *a, const void *b) {
return ((Draw_Quad*)a)->z-((Draw_Quad*)b)->z;
}
void test_sort() {
int num_samples = 500;
u64 id_bits = 21;
u64 item_count = 50000;
f64 seconds = 0;
u64 cycles = 0;
Draw_Quad *items = alloc(get_heap_allocator(), (item_count * 2) * sizeof(Draw_Quad));
Draw_Quad *buffer = items + item_count;
for (int a = 0; a < num_samples; a++) {
for (u64 i = 0; i < item_count; i++) {
if (i % 2 == 0) items[i].z = get_random_int_in_range(0, pow(2, id_bits) / 2);
else items[i].z = i;
}
u64 item_size = sizeof(Draw_Quad);
u64 sort_value_offset_in_item = offsetof(Draw_Quad, z);
float64 start_seconds = os_get_elapsed_seconds();
2024-07-17 16:36:13 +02:00
u64 start_cycles = rdtsc();
radix_sort(items, buffer, item_count, item_size, sort_value_offset_in_item, id_bits);
2024-07-17 16:36:13 +02:00
u64 end_cycles = rdtsc();
float64 end_seconds = os_get_elapsed_seconds();
for (u64 i = 1; i < item_count; i++) {
assert(items[i].z >= items[i-1].z, "Failed: not correctly sorted");
}
seconds += end_seconds - start_seconds;
cycles += end_cycles - start_cycles;
}
print("Radix sort took on average %llu cycles and %.2f ms\n", cycles / num_samples, (seconds * 1000.0) / (float64)num_samples);
seconds = 0;
cycles = 0;
for (int a = 0; a < num_samples; a++) {
for (u64 i = 0; i < item_count; i++) {
if (i % 2 == 0) items[i].z = get_random_int_in_range(0, pow(2, id_bits) / 2);
else items[i].z = i;
}
u64 item_size = sizeof(Draw_Quad);
u64 sort_value_offset_in_item = offsetof(Draw_Quad, z);
float64 start_seconds = os_get_elapsed_seconds();
2024-07-17 16:36:13 +02:00
u64 start_cycles = rdtsc();
merge_sort(items, buffer, item_count, item_size, compare_draw_quads);
2024-07-17 16:36:13 +02:00
u64 end_cycles = rdtsc();
float64 end_seconds = os_get_elapsed_seconds();
for (u64 i = 1; i < item_count; i++) {
assert(items[i].z >= items[i-1].z, "Failed: not correctly sorted");
}
seconds += end_seconds - start_seconds;
cycles += end_cycles - start_cycles;
}
print("Merge sort took on average %llu cycles and %.2f ms\n", cycles / num_samples, (seconds * 1000.0) / (float64)num_samples);
}
2024-07-22 01:06:21 +02:00
#endif /* OOGABOOGA_HEADLESS */
typedef struct Test_Thing {
int foo;
float bar;
} Test_Thing;
void test_growing_array() {
Test_Thing *things = 0;
growing_array_init((void**)&things, sizeof(Test_Thing), get_heap_allocator());
Test_Thing new_thing;
new_thing.foo = 5;
new_thing.bar = 420.69;
growing_array_add((void**)&things, &new_thing);
assert(growing_array_get_valid_count(things) == 1, "Failed: growing_array_get_valid_count");
new_thing.foo = 1;
new_thing.bar = 123.45;
growing_array_add((void**)&things, &new_thing);
assert(growing_array_get_valid_count(things) == 2, "Failed: growing_array_get_valid_count");
assert(things[0].foo == 5 && floats_roughly_match(things[0].bar, 420.69), "Failed: growing_array_add");
assert(things[1].foo == 1 && floats_roughly_match(things[1].bar, 123.45), "Failed: growing_array_add");
growing_array_ordered_remove_by_index((void**)&things, 0);
assert(things[0].foo == 1 && floats_roughly_match(things[0].bar, 123.45), "Failed: growing_array_ordered_remove_by_index");
assert(growing_array_get_valid_count(things) == 1, "Failed: growing_array_get_valid_count");
new_thing.foo = 5;
new_thing.bar = 420.69;
growing_array_add((void**)&things, &new_thing);
assert(things[1].foo == 5 && floats_roughly_match(things[1].bar, 420.69), "Failed: growing_array_add");
assert(growing_array_get_valid_count(things) == 2, "Failed: growing_array_get_valid_count");
growing_array_unordered_remove_by_index((void**)&things, 0);
assert(things[0].foo == 5 && floats_roughly_match(things[0].bar, 420.69), "Failed: growing_array_unordered_remove_by_index");
assert(growing_array_get_valid_count(things) == 1, "Failed: growing_array_get_valid_count");
for (u32 i = 0; i < 100; i += 1) {
new_thing.foo = i;
new_thing.bar = i * 4.0;
growing_array_add((void**)&things, &new_thing);
}
assert(growing_array_get_valid_count(things) == 101, "Failed: growing_array_get_valid_count");
// Unordered remove by pointer
Test_Thing *thing = &things[50];
Test_Thing copy = *thing;
bool found = growing_array_unordered_remove_by_pointer((void**)&things, thing);
assert(found, "Failed: growing_array_unordered_remove_by_pointer");
assert(!bytes_match(&copy, thing, sizeof(Test_Thing)), "Failed: growing_array_unordered_remove_by_pointer");
// Ordered remove by pointer
thing = &things[50];
copy = *thing;
found = growing_array_ordered_remove_by_pointer((void**)&things, thing);
assert(found, "Failed: growing_array_unordered_remove_by_pointer");
assert(!bytes_match(&copy, thing, sizeof(Test_Thing)), "Failed: growing_array_unordered_remove_by_pointer");
assert(growing_array_get_valid_count(things) == 99, "Failed: growing_array_get_valid_count");
}
void oogabooga_run_tests() {
print("Testing growing array... ");
test_growing_array();
print("OK!\n");
2024-07-11 20:38:26 +02:00
print("Testing allocator... ");
test_allocator(true);
print("OK!\n");
print("Testing threads... ");
test_threads();
print("OK!\n");
print("Testing strings... ");
test_strings();
print("OK!\n");
print("Testing file IO... ");
test_file_io();
print("OK!\n");
2024-07-04 12:18:16 +02:00
print("Testing linmath... ");
test_linmath();
print("OK!\n");
print("Testing intmath... ");
test_intmath();
print("OK!\n");
print("Testing simd... ");
test_simd();
print("OK!\n");
2024-07-06 15:50:16 +02:00
print("Testing hash table... ");
test_hash_table();
print("OK!\n");
print("Testing random distribution... ");
test_random_distribution();
print("OK!\n");
print("Testing mutex... ");
test_mutex();
2024-07-11 20:38:26 +02:00
print("OK!\n");
2024-07-22 01:06:21 +02:00
#ifndef OOGABOOGA_HEADLESS
print("Testing radix sort... ");
test_sort();
print("OK!\n");
2024-07-22 01:06:21 +02:00
#endif
2024-07-15 21:40:27 +02:00
print("All tests ok!\n");
}