#include "LayerStackStore.h" #include "RuntimeStateJson.h" #include "ShaderPackageCatalog.h" #include #include #include #include #include #include namespace { int gFailures = 0; void Expect(bool condition, const char* message) { if (condition) return; std::cerr << "FAIL: " << message << "\n"; ++gFailures; } std::filesystem::path MakeTestRoot() { const auto stamp = std::chrono::steady_clock::now().time_since_epoch().count(); std::filesystem::path root = std::filesystem::temp_directory_path() / ("video-shader-runtime-subsystem-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; } void WriteShaderPackage(const std::filesystem::path& root, const std::string& directoryName, const std::string& manifest) { const std::filesystem::path packageRoot = root / directoryName; WriteFile(packageRoot / "shader.json", manifest); WriteFile(packageRoot / "shader.slang", "float4 shadeVideo(float2 uv) { return float4(uv, 0.0, 1.0); }\n"); } ShaderPackageCatalog BuildCatalog(const std::filesystem::path& root) { ShaderPackageCatalog catalog; std::string error; Expect(catalog.Scan(root, 4, error), "shader package catalog scans test packages"); Expect(error.empty(), "catalog scan does not report an error"); return catalog; } void TestLayerDefaultsAndCrud() { const std::filesystem::path root = MakeTestRoot(); WriteShaderPackage(root, "alpha", R"({ "id": "alpha", "name": "Alpha", "parameters": [ { "id": "gain", "label": "Gain", "type": "float", "default": 0.25, "min": 0, "max": 1 }, { "id": "enabled", "label": "Enabled", "type": "bool", "default": true } ] })"); WriteShaderPackage(root, "beta", R"({ "id": "beta", "name": "Beta", "parameters": [ { "id": "mode", "label": "Mode", "type": "enum", "default": "soft", "options": [ { "value": "soft", "label": "Soft" }, { "value": "hard", "label": "Hard" } ] } ] })"); ShaderPackageCatalog catalog = BuildCatalog(root); LayerStackStore layers; std::string error; Expect(layers.CreateLayer(catalog, "alpha", error), "layer store creates a layer for a known shader"); Expect(layers.LayerCount() == 1, "created layer is stored"); Expect(!layers.CreateLayer(catalog, "missing", error), "layer store rejects unknown shaders"); LayerStackStore::StoredParameterSnapshot snapshot; Expect(layers.TryGetParameterById(catalog, layers.Layers()[0].id, "gain", snapshot, error), "parameter lookup by id succeeds"); Expect(snapshot.currentValue.numberValues.size() == 1 && snapshot.currentValue.numberValues[0] == 0.25, "default float value is persisted"); ShaderParameterValue value; value.numberValues = { 0.75 }; Expect(layers.SetParameterValue(layers.Layers()[0].id, "gain", value, error), "parameter value can be updated"); Expect(layers.TryGetParameterById(catalog, layers.Layers()[0].id, "gain", snapshot, error), "updated parameter can be read"); Expect(snapshot.currentValue.numberValues.size() == 1 && snapshot.currentValue.numberValues[0] == 0.75, "updated float value is retained"); Expect(layers.SetLayerShaderSelection(catalog, layers.Layers()[0].id, "beta", error), "layer shader selection can change"); Expect(layers.Layers()[0].shaderId == "beta", "new shader id is stored"); Expect(layers.TryGetParameterById(catalog, layers.Layers()[0].id, "mode", snapshot, error), "new shader defaults are applied"); Expect(snapshot.currentValue.enumValue == "soft", "enum default is applied after shader change"); std::filesystem::remove_all(root); } void TestMoveClassificationAndPresetLoad() { const std::filesystem::path root = MakeTestRoot(); WriteShaderPackage(root, "alpha", R"({ "id": "alpha", "name": "Alpha", "parameters": [{ "id": "gain", "label": "Gain", "type": "float", "default": 0.5, "min": 0, "max": 1 }] })"); WriteShaderPackage(root, "beta", R"({ "id": "beta", "name": "Beta", "parameters": [] })"); ShaderPackageCatalog catalog = BuildCatalog(root); LayerStackStore layers; std::string error; Expect(layers.CreateLayer(catalog, "alpha", error), "first test layer is created"); Expect(layers.CreateLayer(catalog, "beta", error), "second test layer is created"); const std::string firstLayerId = layers.Layers()[0].id; const std::string secondLayerId = layers.Layers()[1].id; bool shouldMove = true; Expect(layers.ResolveLayerMove(firstLayerId, -1, shouldMove, error), "top layer move up is classified"); Expect(!shouldMove, "top layer move up is a no-op"); Expect(layers.ResolveLayerMove(firstLayerId, 1, shouldMove, error), "top layer move down is classified"); Expect(shouldMove, "top layer move down should move"); Expect(layers.MoveLayer(firstLayerId, 1, error), "top layer moves down"); Expect(layers.Layers()[0].id == secondLayerId && layers.Layers()[1].id == firstLayerId, "layer order changed after move"); JsonValue preset = layers.BuildStackPresetValue(catalog, "Look One"); LayerStackStore loaded; Expect(loaded.LoadStackPresetValue(catalog, preset, error), "stack preset value loads into a fresh layer store"); Expect(loaded.LayerCount() == 2, "loaded preset preserves layer count"); Expect(loaded.Layers()[0].shaderId == "beta" && loaded.Layers()[1].shaderId == "alpha", "loaded preset preserves shader order"); Expect(loaded.Layers()[0].id != secondLayerId, "loaded preset generates fresh layer ids"); std::filesystem::remove_all(root); } void TestRuntimeStateJsonReadModelSerialization() { ShaderPackage package; package.id = "alpha"; package.displayName = "Alpha"; package.feedback.enabled = true; package.feedback.writePassId = "main"; package.temporal.enabled = true; package.temporal.historySource = TemporalHistorySource::Source; package.temporal.requestedHistoryLength = 3; package.temporal.effectiveHistoryLength = 3; ShaderParameterDefinition gain; gain.id = "gain"; gain.label = "Gain"; gain.type = ShaderParameterType::Float; gain.defaultNumbers = { 0.5 }; gain.minNumbers = { 0.0 }; gain.maxNumbers = { 1.0 }; package.parameters.push_back(gain); LayerStackStore::LayerPersistentState layer; layer.id = "layer-1"; layer.shaderId = "alpha"; ShaderParameterValue gainValue; gainValue.numberValues = { 0.8 }; layer.parameterValues["gain"] = gainValue; JsonValue layersJson = RuntimeStateJson::SerializeLayerStack({ layer }, { { "alpha", package } }); Expect(layersJson.isArray() && layersJson.asArray().size() == 1, "runtime state layer serialization emits one layer"); const JsonValue& layerJson = layersJson.asArray()[0]; Expect(layerJson.find("shaderName") && layerJson.find("shaderName")->asString() == "Alpha", "serialized layer includes shader display name"); Expect(layerJson.find("temporal") && layerJson.find("temporal")->isObject(), "serialized layer includes temporal metadata"); Expect(layerJson.find("feedback") && layerJson.find("feedback")->isObject(), "serialized layer includes feedback metadata"); const JsonValue* parameters = layerJson.find("parameters"); Expect(parameters && parameters->isArray() && parameters->asArray().size() == 1, "serialized layer includes parameter metadata"); const JsonValue* value = parameters->asArray()[0].find("value"); Expect(value && value->asNumber() == 0.8, "serialized parameter includes current value"); } } int main() { TestLayerDefaultsAndCrud(); TestMoveClassificationAndPresetLoad(); TestRuntimeStateJsonReadModelSerialization(); if (gFailures != 0) { std::cerr << gFailures << " RuntimeSubsystem test failure(s).\n"; return 1; } std::cout << "RuntimeSubsystem tests passed.\n"; return 0; }