#include #include "runtime/config.h" #include "runtime/ds.h" #include "runtime/mem_arena.h" #include "runtime/runtime.h" #include "gfx/gfx.h" #include "gfx/render_list.h" #include "gfx/renderer_api.h" extern rt_cvar rt_Renderer; /* Check basic relative pointer behaviour */ static rt_result RelPtrTest(void) { char buf[sizeof(rt_relptr) + sizeof(unsigned int)]; rt_relptr *ptr = (rt_relptr *)buf; unsigned int *target = (unsigned int *)&buf[sizeof(rt_relptr)]; *target = 42; rtSetRelptr(ptr, target); void *resolved = rtResolveRelptr(ptr); if ((uintptr_t)resolved != (uintptr_t)target) return 1; if (*(unsigned int *)resolved != *target) return 2; return RT_SUCCESS; } static rt_result NegRelPtrTest(void) { char buf[sizeof(unsigned int) + sizeof(rt_relptr)]; unsigned int *target = (unsigned int *)buf; rt_relptr *ptr = (rt_relptr *)&buf[sizeof(unsigned int)]; *target = 42; rtSetRelptr(ptr, target); void *resolved = rtResolveRelptr(ptr); if ((uintptr_t)resolved != (uintptr_t)target) return 1; if (*(unsigned int *)resolved != *target) return 2; return RT_SUCCESS; } static rt_result SetupRenderListFixture(void) { rt_result res = rtInitRuntime(); if (res != RT_SUCCESS) return res; rt_Renderer.s = "null"; rt_renderer_init_info info = {0}; rtInitGFX(&info); return res; } static void TeardownRenderListFixture(void) { rtShutdownGFX(); rtShutdownRuntime(); } /* Check render list interface */ static rt_result PushRenderList(void) { typedef struct { int a; float b; } dummy_type; rt_render_object_type type = rtRegisterRenderObjectType(sizeof(dummy_type), "DummyType"); rt_create_render_list_result list_res = rtCreateRenderList(type, 43); if (!list_res.ok) { return RT_INVALID_VALUE; } rt_render_list list = list_res.list; dummy_type dummy = {42, 21.f}; rtPushRenderListEntry(&list, &dummy); dummy_type check = RT_GET_RENDER_LIST_ELEMENT(list, dummy_type, 0); if (check.a != dummy.a || check.b != dummy.b) return RT_INVALID_VALUE; return RT_SUCCESS; } static rt_result PushLongRenderList(void) { typedef struct { float p[3]; float v[3]; } dummy_type; rt_render_object_type type = rtRegisterRenderObjectType(sizeof(dummy_type), "DummyType"); rt_create_render_list_result list_res = rtCreateRenderList(type, 43); if (!list_res.ok) return RT_INVALID_VALUE; rt_render_list list = list_res.list; for (uint32_t i = 0; i < 512; ++i) { dummy_type dummy; for (int j = 0; j < 3; ++j) dummy.v[j] = dummy.p[j] = (float)i; rtPushRenderListEntry(&list, &dummy); dummy_type check = RT_GET_RENDER_LIST_ELEMENT(list, dummy_type, i); for (int j = 0; j < 3; ++j) { if (check.p[j] != (float)i || check.v[j] != (float)i) return RT_INVALID_VALUE; } } for (uint32_t i = 0; i < 512; ++i) { dummy_type check = RT_GET_RENDER_LIST_ELEMENT(list, dummy_type, i); for (int j = 0; j < 3; ++j) { if (check.p[j] != (float)i || check.v[j] != (float)i) return RT_INVALID_VALUE; } } return RT_SUCCESS; } static rt_result HashTableBasics(void) { { uint64_t mem[128]; rt_hashtable ht = rtCreateHashtable(64, mem, NULL, NULL); for (uint64_t i = 0; i < 64; ++i) { if (rtHashtableInsert(&ht, i, i) != RT_SUCCESS) return RT_UNKNOWN_ERROR; uint64_t found = rtHashtableLookup(&ht, i, UINT64_MAX); if (found != i) return RT_INVALID_VALUE; } } { rt_create_arena_result arena_res = rtCreateArena(NULL, RT_KB(4)); if (!arena_res.ok) return RT_OUT_OF_MEMORY; rt_arena arena = arena_res.arena; void *mem = rtArenaPush(&arena, RT_HASH_TABLE_MEMORY_REQUIRED(64)); if (!mem) return RT_OUT_OF_MEMORY; rt_hashtable ht = rtCreateHashtable(64, mem, rtHashtableGrowMemoryFromArena, &arena); for (uint64_t i = 0; i < 64; ++i) { if (rtHashtableInsert(&ht, 256+i, i) != RT_SUCCESS) return RT_UNKNOWN_ERROR; uint64_t found = rtHashtableLookup(&ht, 256+i, UINT64_MAX); if (found != i) return RT_INVALID_VALUE; } rtReleaseArena(&arena); } return RT_SUCCESS; } /* Scaffolding * * Run all the test cases, output if they passed or failed. */ typedef rt_result rt_fixture_setup_fn(void); typedef void rt_fixture_teardown_fn(void); typedef struct { const char *name; bool is_initialized; rt_fixture_setup_fn *setup; rt_fixture_teardown_fn *teardown; } rt_test_fixture; typedef rt_result rt_test_fnc(void); typedef struct { const char *name; rt_test_fnc *fnc; rt_test_fixture *fixture; } rt_test_case; #define TEST_CASE(fn) \ { .name = #fn, .fnc = fn, .fixture = NULL, } #define TEST_CASE_FIXTURE(fn, _fixture) \ { .name = #fn, .fnc = fn, .fixture = &_fixture } /* X-Macro to create named fixtures */ /* Add more fixtures here*/ #define TEST_FIXTURE_LIST \ TEST_FIXTURE(render_list_fixture, SetupRenderListFixture, TeardownRenderListFixture) #define TEST_FIXTURE(n, setup_fn, teardown_fn) \ rt_test_fixture n = {.name = #n, \ .is_initialized = false, \ .setup = setup_fn, \ .teardown = teardown_fn}; TEST_FIXTURE_LIST #undef TEST_FIXTURE #define TEST_FIXTURE(n, _s, _t) &n static rt_test_fixture *_test_fixtures[] = {TEST_FIXTURE_LIST}; static rt_test_case _test_cases[] = {TEST_CASE(RelPtrTest), TEST_CASE(NegRelPtrTest), TEST_CASE_FIXTURE(PushRenderList, render_list_fixture), TEST_CASE_FIXTURE(PushLongRenderList, render_list_fixture), TEST_CASE(HashTableBasics)}; int main() { int out = 0; for (size_t i = 0; i < RT_ARRAY_COUNT(_test_cases); ++i) { if (_test_cases[i].fixture) { rt_test_fixture *fixture = _test_cases[i].fixture; if (!fixture->is_initialized) { printf("[SETUP %s] ... ", fixture->name); rt_result res = fixture->setup(); if (res == RT_SUCCESS) { printf("OK\n"); fixture->is_initialized = true; } else { printf("FAILED (%u)\n", res); ++out; printf("Cannot run test case %s because the setup of fixture %s failed!\n", _test_cases[i].name, fixture->name); continue; } } } printf("[%s] ... ", _test_cases[i].name); rt_result res = _test_cases[i].fnc(); if (res == RT_SUCCESS) { printf("OK\n"); } else { printf("FAILED (%u)\n", res); ++out; } } /* Teardown fixtures */ for (size_t i = 0; i < RT_ARRAY_COUNT(_test_fixtures); ++i) { if (_test_fixtures[i]->is_initialized) { printf("[TEARDOWN %s] ... ", _test_fixtures[i]->name); _test_fixtures[i]->teardown(); printf("DONE\n"); } } return out; }