Files
video-shader-toys/tests/RuntimeSubsystemTests.cpp
Aiden 41677b71ec
Some checks failed
CI / React UI Build (push) Successful in 10s
CI / Native Windows Build And Tests (push) Successful in 2m18s
CI / Windows Release Package (push) Has been cancelled
Finished phase 1
2026-05-11 02:32:13 +10:00

201 lines
7.6 KiB
C++

#include "LayerStackStore.h"
#include "RuntimeStateJson.h"
#include "ShaderPackageCatalog.h"
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <string>
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;
}