#include "RuntimeLayerModel.h" #include #include #include #include #include namespace { int gFailures = 0; void Expect(bool condition, const std::string& message) { if (condition) return; ++gFailures; std::cerr << "FAIL: " << message << "\n"; } std::filesystem::path MakeTestRoot() { const auto stamp = std::chrono::steady_clock::now().time_since_epoch().count(); const std::filesystem::path root = std::filesystem::temp_directory_path() / ("render-cadence-layer-model-tests-" + std::to_string(stamp)); std::filesystem::create_directories(root); return root; } void WriteFile(const std::filesystem::path& path, const std::string& contents) { std::filesystem::create_directories(path.parent_path()); std::ofstream output(path, std::ios::binary); output << contents; } RenderCadenceCompositor::SupportedShaderCatalog MakeCatalog(std::filesystem::path& root) { root = MakeTestRoot(); WriteFile(root / "solid" / "shader.slang", "float4 shadeVideo(float2 uv) { return float4(uv, 0.0, 1.0); }\n"); WriteFile(root / "solid" / "shader.json", R"({ "id": "solid", "name": "Solid", "description": "Solid test shader", "category": "Tests", "entryPoint": "shadeVideo", "parameters": [ { "id": "gain", "label": "Gain", "type": "float", "default": 0.5 } ] })"); RenderCadenceCompositor::SupportedShaderCatalog catalog; std::string error; Expect(catalog.Load(root, 4, error), error.empty() ? "catalog loads test shader" : error); return catalog; } void TestSingleLayerLifecycle() { std::filesystem::path root; RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root); RenderCadenceCompositor::RuntimeLayerModel model; std::string error; Expect(model.InitializeSingleLayer(catalog, "solid", error), "model initializes a supported startup shader"); Expect(model.FirstLayerId() == "runtime-layer-1", "startup layer id is stable"); Expect(model.MarkBuildStarted(model.FirstLayerId(), "build started", error), "build start updates the layer"); RenderCadenceCompositor::RuntimeLayerModelSnapshot snapshot = model.Snapshot(); Expect(snapshot.displayLayers.size() == 1, "snapshot exposes the display layer"); Expect(snapshot.displayLayers[0].buildState == RenderCadenceCompositor::RuntimeLayerBuildState::Pending, "started layer is pending"); Expect(!snapshot.displayLayers[0].renderReady, "started layer is not render-ready yet"); RuntimeShaderArtifact artifact; artifact.shaderId = "solid"; artifact.displayName = "Solid"; artifact.fragmentShaderSource = "void main(){}"; artifact.message = "build ready"; Expect(model.MarkBuildReady(artifact, error), "ready artifact updates the matching layer"); snapshot = model.Snapshot(); Expect(snapshot.compileSucceeded, "ready layer reports compile success"); Expect(snapshot.displayLayers[0].buildState == RenderCadenceCompositor::RuntimeLayerBuildState::Ready, "ready layer is marked ready"); Expect(snapshot.displayLayers[0].renderReady, "ready layer exposes render readiness"); Expect(snapshot.renderLayers.size() == 1, "ready layer produces one render layer artifact"); Expect(snapshot.renderLayers[0].artifact.shaderId == "solid", "render layer carries the artifact"); std::filesystem::remove_all(root); } void TestRejectsUnsupportedStartupShader() { std::filesystem::path root; RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root); RenderCadenceCompositor::RuntimeLayerModel model; std::string error; Expect(!model.InitializeSingleLayer(catalog, "missing", error), "model rejects unsupported shader ids"); Expect(!error.empty(), "unsupported shader rejection explains the problem"); Expect(model.Snapshot().displayLayers.empty(), "rejected startup shader leaves no display layer"); std::filesystem::remove_all(root); } void TestBuildFailureStaysDisplaySide() { std::filesystem::path root; RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root); RenderCadenceCompositor::RuntimeLayerModel model; std::string error; Expect(model.InitializeSingleLayer(catalog, "solid", error), "model initializes for failure test"); Expect(model.MarkBuildFailed(model.FirstLayerId(), "compile failed", error), "build failure updates the layer"); const RenderCadenceCompositor::RuntimeLayerModelSnapshot snapshot = model.Snapshot(); Expect(!snapshot.compileSucceeded, "failed layer reports compile failure"); Expect(snapshot.displayLayers[0].buildState == RenderCadenceCompositor::RuntimeLayerBuildState::Failed, "failed layer is marked failed"); Expect(!snapshot.displayLayers[0].renderReady, "failed layer is not render-ready"); Expect(snapshot.renderLayers.empty(), "failed layer does not produce a render artifact"); std::filesystem::remove_all(root); } void TestAddAndRemoveLayers() { std::filesystem::path root; RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root); RenderCadenceCompositor::RuntimeLayerModel model; std::string error; std::string firstLayerId; std::string secondLayerId; Expect(model.AddLayer(catalog, "solid", firstLayerId, error), "first layer can be added"); Expect(model.AddLayer(catalog, "solid", secondLayerId, error), "second layer can be added"); Expect(firstLayerId != secondLayerId, "added layers receive distinct ids"); Expect(model.Snapshot().displayLayers.size() == 2, "added layers appear in display snapshot"); Expect(model.RemoveLayer(firstLayerId, error), "existing layer can be removed"); RenderCadenceCompositor::RuntimeLayerModelSnapshot snapshot = model.Snapshot(); Expect(snapshot.displayLayers.size() == 1, "removed layer leaves snapshot"); Expect(snapshot.displayLayers[0].id == secondLayerId, "remaining layer identity is preserved"); Expect(!model.RemoveLayer(firstLayerId, error), "removed layer cannot be removed twice"); std::filesystem::remove_all(root); } void TestLayerControlsUpdateDisplayAndRenderModels() { std::filesystem::path root; RenderCadenceCompositor::SupportedShaderCatalog catalog = MakeCatalog(root); RenderCadenceCompositor::RuntimeLayerModel model; std::string error; std::string firstLayerId; std::string secondLayerId; Expect(model.AddLayer(catalog, "solid", firstLayerId, error), "first control layer can be added"); Expect(model.AddLayer(catalog, "solid", secondLayerId, error), "second control layer can be added"); Expect(model.SetLayerBypass(firstLayerId, true, error), "bypass can be set"); Expect(model.ReorderLayer(firstLayerId, 1, error), "layer can be reordered"); RenderCadenceCompositor::RuntimeLayerModelSnapshot snapshot = model.Snapshot(); Expect(snapshot.displayLayers[1].id == firstLayerId, "reordered layer moves to requested index"); Expect(snapshot.displayLayers[1].bypass, "bypass state is visible in read model"); JsonValue gainValue(0.75); Expect(model.UpdateParameter(firstLayerId, "gain", gainValue, error), "parameter value can be updated"); snapshot = model.Snapshot(); Expect(snapshot.displayLayers[1].parameterValues.at("gain").numberValues.front() == 0.75, "updated parameter value is visible"); RuntimeShaderArtifact artifact; artifact.layerId = firstLayerId; artifact.shaderId = "solid"; artifact.displayName = "Solid"; artifact.fragmentShaderSource = "void main(){}"; artifact.parameterDefinitions = snapshot.displayLayers[1].parameterDefinitions; artifact.message = "build ready"; Expect(model.MarkBuildReady(artifact, error), "ready artifact keeps layer parameter state"); snapshot = model.Snapshot(); Expect(snapshot.renderLayers.size() == 1, "ready layer produces render model"); Expect(snapshot.renderLayers[0].bypass, "render model carries bypass state"); Expect(snapshot.renderLayers[0].artifact.parameterValues.at("gain").numberValues.front() == 0.75, "render artifact carries updated parameter value"); Expect(model.ResetParameters(firstLayerId, error), "parameters can reset to defaults"); snapshot = model.Snapshot(); Expect(snapshot.displayLayers[1].parameterValues.at("gain").numberValues.front() == 0.5, "reset restores default value"); std::filesystem::remove_all(root); } } int main() { TestSingleLayerLifecycle(); TestRejectsUnsupportedStartupShader(); TestBuildFailureStaysDisplaySide(); TestAddAndRemoveLayers(); TestLayerControlsUpdateDisplayAndRenderModels(); if (gFailures != 0) { std::cerr << gFailures << " RenderCadenceCompositorRuntimeLayerModel test failure(s).\n"; return 1; } std::cout << "RenderCadenceCompositorRuntimeLayerModel tests passed.\n"; return 0; }