#include "services/impl/logger_service.hpp" #include "services/impl/script_engine_service.hpp" #include "services/impl/scene_script_service.hpp" #include "services/impl/shader_script_service.hpp" #include "services/interfaces/i_config_service.hpp" #include #include #include #include #include #include #include namespace { constexpr std::array kIdentityMatrix = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; bool ApproximatelyEqual(float a, float b, float eps = 1e-5f) { return std::fabs(a - b) <= eps; } bool ExpectIdentity(const std::array& actual, const std::string& label, int& failures) { for (size_t i = 0; i < actual.size(); ++i) { if (!ApproximatelyEqual(actual[i], kIdentityMatrix[i])) { std::cerr << label << " differs at index " << i << " (" << actual[i] << " vs " << kIdentityMatrix[i] << ")\n"; ++failures; return false; } } return true; } std::filesystem::path GetTestScriptPath() { auto testDir = std::filesystem::path(__FILE__).parent_path(); return testDir / "scripts" / "unit_cube_logic.lua"; } class StubConfigService final : public sdl3cpp::services::IConfigService { public: StubConfigService() { materialXConfig_.enabled = true; materialXConfig_.useConstantColor = true; materialXConfig_.shaderKey = "test"; materialXConfig_.libraryPath = ResolveMaterialXLibraryPath(); } uint32_t GetWindowWidth() const override { return 1; } uint32_t GetWindowHeight() const override { return 1; } std::filesystem::path GetScriptPath() const override { return {}; } bool IsLuaDebugEnabled() const override { return false; } std::string GetWindowTitle() const override { return ""; } const sdl3cpp::services::InputBindings& GetInputBindings() const override { return inputBindings_; } const sdl3cpp::services::MouseGrabConfig& GetMouseGrabConfig() const override { return mouseGrabConfig_; } const sdl3cpp::services::BgfxConfig& GetBgfxConfig() const override { return bgfxConfig_; } const sdl3cpp::services::MaterialXConfig& GetMaterialXConfig() const override { return materialXConfig_; } const std::vector& GetMaterialXMaterialConfigs() const override { return materialXMaterials_; } const sdl3cpp::services::GuiFontConfig& GetGuiFontConfig() const override { return guiFontConfig_; } const std::string& GetConfigJson() const override { return configJson_; } private: static std::filesystem::path ResolveMaterialXLibraryPath() { auto repoRoot = std::filesystem::path(__FILE__).parent_path().parent_path(); return repoRoot / "MaterialX" / "libraries"; } sdl3cpp::services::InputBindings inputBindings_{}; sdl3cpp::services::MouseGrabConfig mouseGrabConfig_{}; sdl3cpp::services::BgfxConfig bgfxConfig_{}; sdl3cpp::services::MaterialXConfig materialXConfig_{}; std::vector materialXMaterials_{}; sdl3cpp::services::GuiFontConfig guiFontConfig_{}; std::string configJson_{}; }; void Assert(bool condition, const std::string& message, int& failures) { if (!condition) { std::cerr << "test failure: " << message << '\n'; ++failures; } } } // namespace int main() { int failures = 0; auto scriptPath = GetTestScriptPath(); std::cout << "Loading Lua fixture: " << scriptPath << '\n'; try { auto logger = std::make_shared(); auto configService = std::make_shared(); auto engineService = std::make_shared( scriptPath, logger, nullptr, nullptr, nullptr, nullptr, nullptr, configService, false); engineService->Initialize(); sdl3cpp::services::impl::SceneScriptService sceneService(engineService, logger); sdl3cpp::services::impl::ShaderScriptService shaderService(engineService, configService, logger); auto objects = sceneService.LoadSceneObjects(); Assert(objects.size() == 1, "expected exactly one scene object", failures); if (!objects.empty()) { const auto& object = objects.front(); Assert(object.vertices.size() == 3, "scene object should yield three vertices", failures); Assert(object.indices.size() == 3, "scene object should yield three indices", failures); Assert(object.shaderKeys.size() == 1, "shader keys should contain one entry", failures); if (!object.shaderKeys.empty()) { Assert(object.shaderKeys.front() == "test", "shader key should match fixture", failures); } const std::vector expectedIndices{0, 1, 2}; Assert(object.indices == expectedIndices, "indices should be zero-based", failures); Assert(object.computeModelMatrixRef >= 0, "vertex object must keep a Lua reference", failures); auto objectMatrix = sceneService.ComputeModelMatrix(object.computeModelMatrixRef, 0.5f); ExpectIdentity(objectMatrix, "object compute_model_matrix", failures); } auto fallbackMatrix = sceneService.ComputeModelMatrix(-1, 1.0f); ExpectIdentity(fallbackMatrix, "global compute_model_matrix", failures); auto viewState = sceneService.GetViewState(1.33f); ExpectIdentity(viewState.viewProj, "view_projection matrix", failures); auto shaderMap = shaderService.LoadShaderPathsMap(); Assert(shaderMap.size() == 1, "expected a single shader variant", failures); auto testEntry = shaderMap.find("test"); Assert(testEntry != shaderMap.end(), "shader map missing test entry", failures); if (testEntry != shaderMap.end()) { Assert(!testEntry->second.vertexSource.empty(), "vertex shader source missing", failures); Assert(!testEntry->second.fragmentSource.empty(), "fragment shader source missing", failures); } } catch (const std::exception& ex) { std::cerr << "exception during tests: " << ex.what() << '\n'; return 1; } if (failures == 0) { std::cout << "script_engine_tests: PASSED\n"; } else { std::cerr << "script_engine_tests: FAILED (" << failures << " errors)\n"; } return failures; }