#include "hectic.h" #include typedef struct Struct Struct; struct Struct { int a; int b; char *c; Struct *next; }; typedef struct Struct2 Struct2; struct Struct2 { int a; char *c; float f; Struct2 *left; Struct *other; }; typedef enum TestUnionVariant { TEST_UNION_VARIANT_A, TEST_UNION_VARIANT_B, TEST_UNION_VARIANT_C } TestUnionVariant; typedef union TestUnion { int a; char *b; float c; } TestUnion; char *struct_to_debug_str(Arena *arena, char *name, Struct *self, PtrSet *visited) { raise_trace("struct_to_debug_str: name: %s, self: %p, visited: %p", name, self, visited); char *result = arena_alloc(arena, MEM_KiB); STRUCT_TO_DEBUG_STR(arena, result, Struct, name, self, visited, 3, INT_TO_DEBUG_STR(arena, "a", self->a), INT_TO_DEBUG_STR(arena, "b", self->b), struct_to_debug_str(arena, "next", self->next, visited) ); return result; } char *struct2_to_debug_str(Arena *arena, char *name, Struct2 *self, PtrSet *visited) { raise_trace("struct2_to_debug_str: name: %s, self: %p, visited: %p", name, self, visited); char *result = arena_alloc(arena, MEM_KiB); STRUCT_TO_DEBUG_STR(arena, result, Struct2, name, self, visited, 5, INT_TO_DEBUG_STR(arena, "a", self->a), FLOAT_TO_DEBUG_STR(arena, "f", self->f), STRING_TO_DEBUG_STR(arena, "c", self->c), struct_to_debug_str(arena, "other", self->other, visited), struct2_to_debug_str(arena, "left", self->left, visited) ); raise_trace("returning result"); return result; } char *test_union_to_debug_str(Arena *arena, char *name, TestUnion *self, PtrSet *visited, TestUnionVariant active_variant) { raise_trace("test_union_to_debug_str: name: %s, self: %p, visited: %p, active_variant: %zu", name, self, visited, active_variant); char *result = arena_alloc(arena, MEM_KiB); UNION_TO_DEBUG_STR(arena, result, TestUnion, name, self, visited, active_variant, 3, TEST_UNION_VARIANT_A, INT_TO_DEBUG_STR(arena, "a", self->a), TEST_UNION_VARIANT_B, STRING_TO_DEBUG_STR(arena, "b", self->b), TEST_UNION_VARIANT_C, FLOAT_TO_DEBUG_STR(arena, "c", self->c) ); return result; } void test_struct_to_debug_str(Arena *arena) { // Mock a struct with a cycle Struct test_struct = {.a = 1, .b = 2, .next = NULL}; test_struct.next = &test_struct; PtrSet *visited = ptrset_init(arena); char *result = struct_to_debug_str(arena, "struct", &test_struct, visited); raise_notice("result: %s", result); char *check = arena_alloc(arena, MEM_KiB); sprintf(check, "struct Struct struct = {a = 1, b = 2, struct Struct next = {a = 1, b = 2, struct Struct next = %p} %p} %p", (void*)&test_struct, (void*)&test_struct, (void*)&test_struct); raise_notice("check: %s", check); assert(strcmp(result, check) == 0); } void test_struct2_to_debug_str(Arena *arena) { // Mock a struct with some structs inside, null and cycle Struct test_struct = {.a = 1, .b = 2, .next = NULL}; test_struct.next = &test_struct; Struct2 test_struct2 = {.a = 1, .c = "hello", .f = 3.14, .left = NULL, .other = &test_struct}; PtrSet *visited = ptrset_init(arena); char *result = struct2_to_debug_str(arena, "struct2", &test_struct2, visited); raise_notice("result: %s", result); char *check = arena_alloc(arena, MEM_KiB); sprintf(check, "struct Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", struct Struct other = {a = 1, b = 2, struct Struct next = {a = 1, b = 2, struct Struct next = %p} %p} %p, struct Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2); raise_notice("check: %s", check); assert(strcmp(result, check) == 0); } void test_test_union_to_debug_str(Arena *arena) { TestUnion test_union = {.a = 1}; PtrSet *visited = ptrset_init(arena); char *result = test_union_to_debug_str(arena, "test_union", &test_union, visited, TEST_UNION_VARIANT_A); raise_notice("result: %s", result); char *check = arena_alloc(arena, MEM_KiB); sprintf(check, "union TestUnion test_union = {a = 1} %p", (void*)&test_union); raise_notice("check: %s", check); assert(strcmp(result, check) == 0); } #define INDENTED_RESULT \ "struct Struct2 struct2 = {\n" \ " a = 1,\n" \ " f = 3.140000,\n" \ " c = %p \"hello\",\n" \ " struct Struct other = {\n" \ " a = 1,\n" \ " b = 2,\n" \ " struct Struct next = {\n" \ " a = 1,\n" \ " b = 2,\n" \ " struct Struct next = %p\n" \ " } %p\n" \ " } %p,\n" \ " struct Struct2 left = NULL\n" \ "} %p\n" void test_debug_to_indented_str(Arena *arena) { Struct test_struct = {.a = 1, .b = 2, .next = NULL}; test_struct.next = &test_struct; Struct2 test_struct2 = {.a = 1, .c = "hello", .f = 3.14, .left = NULL, .other = &test_struct}; PtrSet *visited = ptrset_init(arena); char *result = struct2_to_debug_str(arena, "struct2", &test_struct2, visited); raise_notice("result: %s", result); char *indented = debug_to_pretty_str(arena, result); raise_notice("indented: \n%s", indented); char *expected = arena_alloc(arena, MEM_KiB); sprintf(expected, INDENTED_RESULT, (void*)test_struct2.c, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2); raise_notice("expected: \n%s", expected); assert(strcmp(indented, expected) == 0); } int main(void) { printf("%sRunning %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); debug_color_mode = COLOR_MODE_DISABLE; logger_init(); Arena arena = arena_init(MEM_MiB); printf("%sTesting struct_to_debug_str%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET)); test_struct_to_debug_str(&arena); printf("%sTesting struct_to_debug_str2%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET)); test_struct2_to_debug_str(&arena); printf("%sTesting test_union_to_debug_str%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET)); test_test_union_to_debug_str(&arena); printf("%sTesting debug_to_indented_str%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET)); test_debug_to_indented_str(&arena); arena_free(&arena); logger_free(); printf("%sAll tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET)); return 0; }