#include "runner.h" #include "../colla.h" #include // Handle Tests UNIT_TEST(os_handle) { oshandle_t zero = os_handle_zero(); ASSERT(!os_handle_valid(zero)); // Create a handle (using file open) arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t test_file = strv_init("test_file.txt"); // Create test file oshandle_t h_write = os_file_open(test_file, OS_FILE_WRITE); ASSERT(os_handle_valid(h_write)); os_file_puts(h_write, strv_init("test content")); os_file_close(h_write); // Open the file and test handle functions oshandle_t h_read = os_file_open(test_file, OS_FILE_READ); ASSERT(os_handle_valid(h_read)); ASSERT(!os_handle_match(h_read, zero)); oshandle_t h_read2 = os_file_open(test_file, OS_FILE_READ); ASSERT(os_handle_valid(h_read2)); ASSERT(!os_handle_match(h_read, h_read2)); os_file_close(h_read); os_file_close(h_read2); os_file_delete(test_file); arena_cleanup(&arena); } // File Operations Tests UNIT_TEST(os_file_basic) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t test_file = strv_init("test_file.txt"); // Delete if exists if (os_file_exists(test_file)) { os_file_delete(test_file); } // Check existence ASSERT(!os_file_exists(test_file)); // Create and write oshandle_t h_write = os_file_open(test_file, OS_FILE_WRITE); ASSERT(os_handle_valid(h_write)); os_file_putc(h_write, 'H'); os_file_puts(h_write, strv_init("ello World")); os_file_close(h_write); // Check existence after creation ASSERT(os_file_exists(test_file)); // Read back oshandle_t h_read = os_file_open(test_file, OS_FILE_READ); ASSERT(os_handle_valid(h_read)); char buffer[12] = {0}; usize read = os_file_read(h_read, buffer, 11); ASSERT(read == 11); ASSERT(strcmp(buffer, "Hello World") == 0); os_file_close(h_read); // Clean up os_file_delete(test_file); ASSERT(!os_file_exists(test_file)); arena_cleanup(&arena); } UNIT_TEST(os_file_seek) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t test_file = strv_init("test_file.txt"); // Create and write oshandle_t h_write = os_file_open(test_file, OS_FILE_WRITE); ASSERT(os_handle_valid(h_write)); os_file_puts(h_write, strv_init("ABCDEFGHIJ")); os_file_close(h_write); // Open for reading oshandle_t h_read = os_file_open(test_file, OS_FILE_READ); ASSERT(os_handle_valid(h_read)); // Seek to position 5 ASSERT(os_file_seek(h_read, 5)); // Read from position 5 char buffer[6] = {0}; usize read = os_file_read(h_read, buffer, 5); ASSERT(read == 5); ASSERT(strcmp(buffer, "FGHIJ") == 0); // Rewind and read from beginning os_file_rewind(h_read); char buffer2[6] = {0}; read = os_file_read(h_read, buffer2, 5); ASSERT(read == 5); ASSERT(strcmp(buffer2, "ABCDE") == 0); // Test file position ASSERT(os_file_tell(h_read) == 5); // Test file size ASSERT(os_file_size(h_read) == 10); // Seek to end ASSERT(os_file_seek_end(h_read)); ASSERT(os_file_is_finished(h_read)); os_file_close(h_read); os_file_delete(test_file); arena_cleanup(&arena); } UNIT_TEST(os_file_read_write_all) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t test_file = strv_init("test_file.txt"); // Write string strview_t test_data = strv_init("This is test data for read/write all functions"); ASSERT(os_file_write_all_str(test_file, test_data)); // Read back as string str_t read_data = os_file_read_all_str(&arena, test_file); ASSERT(str_equals(read_data, str_init(&arena, "This is test data for read/write all functions"))); // Read as buffer buffer_t buffer = os_file_read_all(&arena, test_file); ASSERT(buffer.len == test_data.len); ASSERT(memcmp(buffer.data, test_data.buf, test_data.len) == 0); // Write buffer const char *new_data = "New buffer data"; buffer_t write_buffer = {(u8*)new_data, strlen(new_data)}; ASSERT(os_file_write_all(test_file, write_buffer)); // Read back after buffer write str_t read_new = os_file_read_all_str(&arena, test_file); ASSERT(str_equals(read_new, str_init(&arena, "New buffer data"))); // Clean up os_file_delete(test_file); arena_cleanup(&arena); } UNIT_TEST(os_file_path) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); // Test path splitting strview_t path = strv_init("/path/to/file.txt"); strview_t dir, name, ext; os_file_split_path(path, &dir, &name, &ext); ASSERT(strv_equals(dir, strv_init("/path/to"))); ASSERT(strv_equals(name, strv_init("file"))); ASSERT(strv_equals(ext, strv_init(".txt"))); // Test full path resolution strview_t relative_path = strv_init("test_file.txt"); os_file_write_all_str(relative_path, strv("hello world")); tstr_t full_path = os_file_fullpath(&arena, relative_path); // Can't easily test the exact value, but can verify it's not empty ASSERT(full_path.len > 0); os_file_delete(relative_path); arena_cleanup(&arena); } // Directory Tests UNIT_TEST(os_dir_operations) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); strview_t test_dir = strv_init("test_dir"); // Delete if exists if (os_dir_exists(test_dir)) { os_file_delete(strv("test_dir/test_file.txt")); os_dir_delete(test_dir); } // Create directory ASSERT(os_dir_create(test_dir)); ASSERT(os_dir_exists(test_dir)); // Create test file in directory strview_t test_file_path = strv_init("test_dir/test_file.txt"); oshandle_t h_write = os_file_open(test_file_path, OS_FILE_WRITE); ASSERT(os_handle_valid(h_write)); os_file_puts(h_write, strv_init("test content")); os_file_close(h_write); // Test directory listing dir_t *dir = os_dir_open(&arena, test_dir); ASSERT(os_dir_is_valid(dir)); bool found_file = false; dir_foreach(&arena, entry, dir) { if (str_equals(entry->name, str_init(&arena, "test_file.txt"))) { found_file = true; ASSERT(entry->type == DIRTYPE_FILE); warn(">> %zu", entry->file_size); ASSERT(entry->file_size == 12); // "test content" } } ASSERT(found_file); // Clean up os_file_delete(test_file_path); os_dir_close(dir); // Directory should now be empty, so we can delete it os_dir_delete(test_dir); ASSERT(!os_dir_exists(test_dir)); arena_cleanup(&arena); } // Environment Variable Tests UNIT_TEST(os_env_vars) { arena_t arena = arena_make(ARENA_MALLOC, KB(4)); arena_t scratch = arena_make(ARENA_MALLOC, KB(1)); // Set environment variable strview_t key = strv_init("COLLA_TEST_VAR"); strview_t value = strv_init("test_value"); os_set_env_var(scratch, key, value); // Get environment variable str_t read_value = os_get_env_var(&arena, key); ASSERT(str_equals(read_value, str_init(&arena, "test_value"))); // Get all environment variables os_env_t *env = os_get_env(&arena); ASSERT(env != NULL); arena_cleanup(&scratch); arena_cleanup(&arena); } // Virtual Memory Tests UNIT_TEST(os_virtual_memory) { usize page_size; void *memory = os_reserve(MB(1), &page_size); ASSERT(memory != NULL); ASSERT(page_size > 0); // Commit a page ASSERT(os_commit(memory, 1)); // Write to the committed memory memset(memory, 0x42, os_get_system_info().page_size); // Release the memory ASSERT(os_release(memory, MB(1))); } // Thread Tests static int thread_test_func(u64 thread_id, void *userdata) { int *value = (int*)userdata; (*value)++; return 42; } UNIT_TEST(os_thread) { // Create thread data int value = 0; // Launch thread oshandle_t thread = os_thread_launch(thread_test_func, &value); ASSERT(os_handle_valid(thread)); // Get thread ID u64 thread_id = os_thread_get_id(thread); ASSERT(thread_id != 0); // Join thread int exit_code; ASSERT(os_thread_join(thread, &exit_code)); ASSERT(exit_code == 42); ASSERT(value == 1); } int test_mutex_trylock(u64 id, void *userdata) { oshandle_t mutex = *((oshandle_t*)userdata); return os_mutex_try_lock(mutex); } // Mutex Tests UNIT_TEST(os_mutex) { oshandle_t mutex = os_mutex_create(); ASSERT(os_handle_valid(mutex)); oshandle_t thread = os_thread_launch(test_mutex_trylock, &mutex); // Lock mutex os_mutex_lock(mutex); int locked = 0; os_thread_join(thread, &locked); ASSERT(locked == false); // Unlock os_mutex_unlock(mutex); // Try lock should succeed now ASSERT(os_mutex_try_lock(mutex)); // Unlock again os_mutex_unlock(mutex); // Free mutex os_mutex_free(mutex); } #if !COLLA_NO_CONDITION_VARIABLE // Condition Variable Tests typedef struct { u64 canary_beg; oshandle_t mutex; oshandle_t cond_mutex; oshandle_t cond; int counter; u64 canary_end; } cond_test_data; static int cond_test_thread(u64 thread_id, void *userdata) { cond_test_data *data = (cond_test_data*)userdata; info("%zu %zu", data->canary_beg, data->canary_end); os_mutex_lock(data->mutex); data->counter++; os_mutex_unlock(data->mutex); // Signal the condition os_cond_signal(data->cond); return 0; } UNIT_TEST(os_condition_variable) { cond_test_data data = { .mutex = os_mutex_create(), .cond_mutex = os_mutex_create(), .cond = os_cond_create(), .counter = 0, }; // Lock mutex before launching thread os_mutex_lock(data.mutex); // Launch thread oshandle_t thread = os_thread_launch(cond_test_thread, &data); // Wait for condition with timeout os_mutex_lock(data.cond_mutex); os_cond_wait(data.cond, data.cond_mutex, 1000); os_mutex_unlock(data.cond_mutex); // We should have the lock again, and counter should be 1 ASSERT(data.counter == 1); // Unlock and cleanup // os_mutex_unlock(data.mutex); os_thread_join(thread, NULL); os_mutex_free(data.mutex); os_mutex_free(data.cond_mutex); os_cond_free(data.cond); } #endif