#include "RuntimeStatePersistence.h" #include #include #include #include #include #include #include namespace { int gFailures = 0; using RenderCadenceCompositor::RuntimeLayerModelSnapshot; using RenderCadenceCompositor::RuntimeLayerReadModel; using RenderCadenceCompositor::SerializeRuntimeStateSnapshot; 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() / ("render-cadence-runtime-state-persistence-tests-" + std::to_string(stamp)); std::filesystem::create_directories(root); return root; } std::string ReadTextFile(const std::filesystem::path& path) { std::ifstream input(path, std::ios::binary); std::ostringstream buffer; buffer << input.rdbuf(); return buffer.str(); } ShaderParameterDefinition FloatParameter() { ShaderParameterDefinition definition; definition.id = "gain"; definition.type = ShaderParameterType::Float; return definition; } ShaderParameterDefinition BooleanParameter() { ShaderParameterDefinition definition; definition.id = "enabled"; definition.type = ShaderParameterType::Boolean; return definition; } RuntimeLayerModelSnapshot MakeSnapshot(const std::string& layerId, double gain, bool enabled) { RuntimeLayerReadModel layer; layer.id = layerId; layer.shaderId = "solid"; layer.shaderName = "Solid"; layer.bypass = !enabled; layer.parameterDefinitions = { FloatParameter(), BooleanParameter() }; ShaderParameterValue gainValue; gainValue.numberValues = { gain }; layer.parameterValues["gain"] = gainValue; ShaderParameterValue enabledValue; enabledValue.booleanValue = enabled; layer.parameterValues["enabled"] = enabledValue; RuntimeLayerModelSnapshot snapshot; snapshot.displayLayers.push_back(layer); return snapshot; } void TestRuntimeStateSerialization() { const std::string serialized = SerializeRuntimeStateSnapshot(MakeSnapshot("layer-a", 0.75, false)); JsonValue parsed; std::string error; Expect(ParseJson(serialized, parsed, error), "serialized runtime state parses"); const JsonValue* layers = parsed.find("layers"); Expect(layers && layers->isArray() && layers->asArray().size() == 1, "serialized runtime state contains one layer"); const JsonValue& layer = layers->asArray().front(); Expect(layer.find("id") && layer.find("id")->asString() == "layer-a", "serialized runtime state preserves layer id"); Expect(layer.find("shaderId") && layer.find("shaderId")->asString() == "solid", "serialized runtime state preserves shader id"); Expect(layer.find("bypass") && layer.find("bypass")->asBoolean() == true, "serialized runtime state preserves bypass flag"); const JsonValue* values = layer.find("parameterValues"); Expect(values && values->isObject(), "serialized runtime state contains parameter values"); Expect(values->find("gain") && values->find("gain")->asNumber() == 0.75, "serialized runtime state preserves float values"); Expect(values->find("enabled") && values->find("enabled")->asBoolean() == false, "serialized runtime state preserves boolean values"); } void TestRuntimeStateWriterFlushesLatestSnapshot() { const std::filesystem::path root = MakeTestRoot(); const std::filesystem::path path = root / "runtime_state.json"; RenderCadenceCompositor::RuntimeStatePersistenceWriter writer; writer.Start(path, std::chrono::milliseconds(1000)); writer.RequestSave(MakeSnapshot("layer-old", 0.25, true)); writer.RequestSave(MakeSnapshot("layer-new", 0.9, false)); writer.Stop(true); JsonValue parsed; std::string error; Expect(ParseJson(ReadTextFile(path), parsed, error), "flushed runtime state parses"); const JsonValue& layer = parsed.find("layers")->asArray().front(); Expect(layer.find("id")->asString() == "layer-new", "writer flushes the latest pending snapshot"); Expect(layer.find("parameterValues")->find("gain")->asNumber() == 0.9, "writer flush keeps latest parameter value"); std::filesystem::remove_all(root); } void TestRuntimeStateWriterDebouncesAndWritesInBackground() { const std::filesystem::path root = MakeTestRoot(); const std::filesystem::path path = root / "nested" / "runtime_state.json"; { RenderCadenceCompositor::RuntimeStatePersistenceWriter writer; writer.Start(path, std::chrono::milliseconds(20)); writer.RequestSave(MakeSnapshot("layer-bg", 0.5, true)); std::this_thread::sleep_for(std::chrono::milliseconds(120)); writer.Stop(true); } JsonValue parsed; std::string error; Expect(ParseJson(ReadTextFile(path), parsed, error), "background runtime state write parses"); Expect(parsed.find("layers")->asArray().front().find("id")->asString() == "layer-bg", "background writer creates the runtime state file"); std::filesystem::remove_all(root); } } int main() { TestRuntimeStateSerialization(); TestRuntimeStateWriterFlushesLatestSnapshot(); TestRuntimeStateWriterDebouncesAndWritesInBackground(); if (gFailures != 0) { std::cerr << gFailures << " RuntimeStatePersistence test failure(s).\n"; return 1; } std::cout << "RuntimeStatePersistence tests passed.\n"; return 0; }