More http post end points filled
This commit is contained in:
@@ -106,6 +106,39 @@ void TestKnownPostEndpointReturnsActionError()
|
||||
Expect(response.body.find("not implemented") != std::string::npos, "unimplemented post reports diagnostic");
|
||||
}
|
||||
|
||||
void TestLayerPostEndpointsUseCallbacks()
|
||||
{
|
||||
using namespace RenderCadenceCompositor;
|
||||
|
||||
HttpControlServer server;
|
||||
HttpControlServerCallbacks callbacks;
|
||||
callbacks.addLayer = [](const std::string& body) {
|
||||
Expect(body.find("solid") != std::string::npos, "add callback receives request body");
|
||||
return ControlActionResult{ true, std::string() };
|
||||
};
|
||||
callbacks.removeLayer = [](const std::string& body) {
|
||||
Expect(body.find("runtime-layer-1") != std::string::npos, "remove callback receives request body");
|
||||
return ControlActionResult{ false, "Unknown layer id." };
|
||||
};
|
||||
server.SetCallbacksForTest(callbacks);
|
||||
|
||||
HttpControlServer::HttpRequest addRequest;
|
||||
addRequest.method = "POST";
|
||||
addRequest.path = "/api/layers/add";
|
||||
addRequest.body = "{\"shaderId\":\"solid\"}";
|
||||
const HttpControlServer::HttpResponse addResponse = server.RouteRequestForTest(addRequest);
|
||||
ExpectEquals(addResponse.status, "200 OK", "add layer callback success returns 200");
|
||||
Expect(addResponse.body.find("\"ok\":true") != std::string::npos, "add layer callback returns action success");
|
||||
|
||||
HttpControlServer::HttpRequest removeRequest;
|
||||
removeRequest.method = "POST";
|
||||
removeRequest.path = "/api/layers/remove";
|
||||
removeRequest.body = "{\"layerId\":\"runtime-layer-1\"}";
|
||||
const HttpControlServer::HttpResponse removeResponse = server.RouteRequestForTest(removeRequest);
|
||||
ExpectEquals(removeResponse.status, "400 Bad Request", "remove layer callback failure returns 400");
|
||||
Expect(removeResponse.body.find("Unknown layer id.") != std::string::npos, "remove layer callback returns diagnostic");
|
||||
}
|
||||
|
||||
void TestUnknownEndpointReturns404()
|
||||
{
|
||||
using namespace RenderCadenceCompositor;
|
||||
@@ -126,6 +159,7 @@ int main()
|
||||
TestStateEndpointUsesCallback();
|
||||
TestRootServesUiIndex();
|
||||
TestKnownPostEndpointReturnsActionError();
|
||||
TestLayerPostEndpointsUseCallbacks();
|
||||
TestUnknownEndpointReturns404();
|
||||
|
||||
if (gFailures != 0)
|
||||
|
||||
163
tests/RenderCadenceCompositorRuntimeLayerModelTests.cpp
Normal file
163
tests/RenderCadenceCompositorRuntimeLayerModelTests.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#include "RuntimeLayerModel.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
TestSingleLayerLifecycle();
|
||||
TestRejectsUnsupportedStartupShader();
|
||||
TestBuildFailureStaysDisplaySide();
|
||||
TestAddAndRemoveLayers();
|
||||
|
||||
if (gFailures != 0)
|
||||
{
|
||||
std::cerr << gFailures << " RenderCadenceCompositorRuntimeLayerModel test failure(s).\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "RenderCadenceCompositorRuntimeLayerModel tests passed.\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "RuntimeStateJson.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -17,29 +19,19 @@ void ExpectContains(const std::string& text, const std::string& fragment, const
|
||||
std::cerr << "FAIL: " << message << "\n";
|
||||
}
|
||||
|
||||
ShaderPackage MakePackage()
|
||||
std::filesystem::path MakeTestRoot()
|
||||
{
|
||||
ShaderPackage package;
|
||||
package.id = "solid-color";
|
||||
package.displayName = "Solid Color";
|
||||
package.description = "A single color shader.";
|
||||
package.category = "Generator";
|
||||
const auto stamp = std::chrono::steady_clock::now().time_since_epoch().count();
|
||||
const std::filesystem::path root = std::filesystem::temp_directory_path() / ("render-cadence-state-json-tests-" + std::to_string(stamp));
|
||||
std::filesystem::create_directories(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
ShaderPassDefinition pass;
|
||||
pass.id = "main";
|
||||
package.passes.push_back(pass);
|
||||
|
||||
ShaderParameterDefinition color;
|
||||
color.id = "color";
|
||||
color.label = "Color";
|
||||
color.description = "Output color.";
|
||||
color.type = ShaderParameterType::Color;
|
||||
color.defaultNumbers = { 1.0, 0.25, 0.5, 1.0 };
|
||||
color.minNumbers = { 0.0, 0.0, 0.0, 0.0 };
|
||||
color.maxNumbers = { 1.0, 1.0, 1.0, 1.0 };
|
||||
color.stepNumbers = { 0.01, 0.01, 0.01, 0.01 };
|
||||
package.parameters.push_back(color);
|
||||
return package;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +45,41 @@ int main()
|
||||
telemetry.renderFps = 59.94;
|
||||
telemetry.shaderBuildsCommitted = 1;
|
||||
|
||||
std::vector<RenderCadenceCompositor::SupportedShaderSummary> shaders = {
|
||||
{ "solid-color", "Solid Color", "A single color shader.", "Generator" }
|
||||
};
|
||||
const ShaderPackage package = MakePackage();
|
||||
const std::filesystem::path root = MakeTestRoot();
|
||||
WriteFile(root / "solid-color" / "shader.slang", "float4 shadeVideo(float2 uv) { return float4(uv, 0.0, 1.0); }\n");
|
||||
WriteFile(root / "solid-color" / "shader.json", R"({
|
||||
"id": "solid-color",
|
||||
"name": "Solid Color",
|
||||
"description": "A single color shader.",
|
||||
"category": "Generator",
|
||||
"entryPoint": "shadeVideo",
|
||||
"parameters": [
|
||||
{
|
||||
"id": "color",
|
||||
"label": "Color",
|
||||
"description": "Output color.",
|
||||
"type": "color",
|
||||
"default": [1.0, 0.25, 0.5, 1.0],
|
||||
"min": [0.0, 0.0, 0.0, 0.0],
|
||||
"max": [1.0, 1.0, 1.0, 1.0],
|
||||
"step": [0.01, 0.01, 0.01, 0.01]
|
||||
}
|
||||
]
|
||||
})");
|
||||
|
||||
RenderCadenceCompositor::SupportedShaderCatalog shaderCatalog;
|
||||
std::string error;
|
||||
ExpectContains(shaderCatalog.Load(root, 4, error) ? "loaded" : error, "loaded", "test shader catalog should load");
|
||||
|
||||
RenderCadenceCompositor::RuntimeLayerModel layerModel;
|
||||
layerModel.InitializeSingleLayer(shaderCatalog, "solid-color", error);
|
||||
RuntimeShaderArtifact artifact;
|
||||
artifact.shaderId = "solid-color";
|
||||
artifact.displayName = "Solid Color";
|
||||
artifact.fragmentShaderSource = "void main(){}";
|
||||
artifact.message = "Runtime shader committed.";
|
||||
layerModel.MarkBuildReady(artifact, error);
|
||||
const RenderCadenceCompositor::RuntimeLayerModelSnapshot layerSnapshot = layerModel.Snapshot();
|
||||
|
||||
const std::string json = RenderCadenceCompositor::RuntimeStateToJson(RenderCadenceCompositor::RuntimeStateJsonInput{
|
||||
config,
|
||||
@@ -64,10 +87,8 @@ int main()
|
||||
8080,
|
||||
true,
|
||||
"DeckLink scheduled output running.",
|
||||
shaders,
|
||||
true,
|
||||
"Runtime shader committed.",
|
||||
&package
|
||||
shaderCatalog,
|
||||
layerSnapshot
|
||||
});
|
||||
|
||||
ExpectContains(json, "\"shaders\":[{\"id\":\"solid-color\"", "state JSON should include supported shaders");
|
||||
@@ -78,6 +99,8 @@ int main()
|
||||
ExpectContains(json, "\"width\":1920", "state JSON should expose output width");
|
||||
ExpectContains(json, "\"height\":1080", "state JSON should expose output height");
|
||||
|
||||
std::filesystem::remove_all(root);
|
||||
|
||||
if (gFailures != 0)
|
||||
{
|
||||
std::cerr << gFailures << " RenderCadenceCompositorRuntimeStateJson test failure(s).\n";
|
||||
|
||||
Reference in New Issue
Block a user