#define TEST_NO_MAIN #include "acutest.h" #include #include #include #include "../include/ECSComponents.h" #include "../include/SceneGraph.h" #include "../include/SceneLoader.h" #include "../include/ModelManager.h" #include "../include/ShaderManager.h" #include "raylib.h" #include "entt.hpp" static bool HasDisplay() { #if defined(_WIN32) return true; #elif defined(__APPLE__) return true; #else const char* display = std::getenv("DISPLAY"); const char* wayland = std::getenv("WAYLAND_DISPLAY"); return (display && *display) || (wayland && *wayland); #endif } static bool InitRaylibHidden() { const char* forceHeadless = std::getenv("SIMIAN_HEADLESS"); if (forceHeadless && forceHeadless[0] == '1') { return false; } if (!HasDisplay()) { return false; } if (!IsWindowReady()) { SetConfigFlags(FLAG_WINDOW_HIDDEN); InitWindow(1, 1, "simian_test_scene"); SetTargetFPS(60); } return IsWindowReady(); } static void ShutdownRaylibHidden() { if (IsWindowReady()) { CloseWindow(); } } void test_scenegraph_world_transform(void) { entt::registry registry; entt::entity parent = registry.create(); entt::entity child = registry.create(); registry.emplace(parent, Vector3{1.0f, 0.0f, 0.0f}, Vector3{2.0f, 2.0f, 2.0f}, 0.0f); registry.emplace(child, Vector3{1.0f, 0.0f, 0.0f}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f); AttachChild(registry, child, parent); UpdateWorldTransforms(registry); auto* childWorld = registry.try_get(child); TEST_CHECK(childWorld != nullptr); if (childWorld) { TEST_CHECK(childWorld->position.x == 3.0f); TEST_CHECK(childWorld->position.y == 0.0f); TEST_CHECK(childWorld->position.z == 0.0f); } } void test_scenegraph_detach(void) { entt::registry registry; entt::entity parent = registry.create(); entt::entity child = registry.create(); AttachChild(registry, child, parent); DetachFromParent(registry, child); auto* childHierarchy = registry.try_get(child); TEST_CHECK(childHierarchy != nullptr); if (childHierarchy) { TEST_CHECK(childHierarchy->parent == entt::null); TEST_CHECK(childHierarchy->prev == entt::null); TEST_CHECK(childHierarchy->next == entt::null); } auto* parentHierarchy = registry.try_get(parent); TEST_CHECK(parentHierarchy != nullptr); if (parentHierarchy) { TEST_CHECK(parentHierarchy->first == entt::null); } } void test_scene_loader_basic(void) { if (!InitRaylibHidden()) { TEST_SKIP("No display available for raylib"); return; } entt::registry registry; ModelManager modelManager; ShaderManager shaderManager; SetSceneContext(®istry, &modelManager, &shaderManager); std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene"; std::filesystem::create_directories(tmpDir); std::filesystem::path scenePath = tmpDir / "scene.toml"; { std::ofstream scene(scenePath); scene << "[[entity]]\n"; scene << "id = \"root\"\n"; scene << "tag = \"Root\"\n"; scene << "position = [0.0, 0.0, 0.0]\n"; scene << "\n"; scene << "[[entity]]\n"; scene << "id = \"parent\"\n"; scene << "parent = \"root\"\n"; scene << "tag = \"Parent\"\n"; scene << "position = [1.0, 0.0, 0.0]\n"; scene << "model = \"cube\"\n"; scene << "model_size = [1.0, 1.0, 1.0]\n"; scene << "color = 0x00FF00FF\n"; scene << "\n"; scene << "[[entity]]\n"; scene << "id = \"child\"\n"; scene << "parent = \"parent\"\n"; scene << "tag = \"Child\"\n"; scene << "position = [0.0, 1.0, 0.0]\n"; scene << "model = \"sphere\"\n"; scene << "radius = 0.5\n"; scene << "color = 0xFF0000FF\n"; } entt::entity root = LoadSceneFromFile(scenePath.string(), true); TEST_CHECK(root != entt::null); entt::entity parentEntity = entt::null; entt::entity childEntity = entt::null; auto tagView = registry.view(); for (auto entity : tagView) { const auto& tag = tagView.get(entity); if (tag.name == "Parent") { parentEntity = entity; } else if (tag.name == "Child") { childEntity = entity; } } TEST_CHECK(parentEntity != entt::null); TEST_CHECK(childEntity != entt::null); if (parentEntity != entt::null && childEntity != entt::null) { auto* childHierarchy = registry.try_get(childEntity); TEST_CHECK(childHierarchy != nullptr); if (childHierarchy) { TEST_CHECK(childHierarchy->parent == parentEntity); } TEST_CHECK(registry.all_of(parentEntity)); TEST_CHECK(registry.all_of(childEntity)); } std::filesystem::remove(scenePath); std::filesystem::remove(tmpDir); ShutdownRaylibHidden(); }