#include "RuntimeParameterUtils.h" #include #include #include namespace { int gFailures = 0; void Expect(bool condition, const char* message) { if (condition) return; std::cerr << "FAIL: " << message << "\n"; ++gFailures; } ShaderParameterDefinition MakeFloatDefinition() { ShaderParameterDefinition definition; definition.id = "gain"; definition.type = ShaderParameterType::Float; definition.defaultNumbers = { 0.5 }; definition.minNumbers = { 0.0 }; definition.maxNumbers = { 1.0 }; return definition; } void TestSafePresetFileStems() { Expect(MakeSafePresetFileStem(" Show Look 01 ") == "show-look-01", "preset names are trimmed and dashed"); Expect(MakeSafePresetFileStem("A__B---C") == "a-b-c", "duplicate separators collapse"); Expect(MakeSafePresetFileStem("../Unsafe Name!") == "unsafe-name", "unsafe punctuation is dropped"); Expect(MakeSafePresetFileStem("!!!") == "", "names without alphanumeric characters become empty"); } void TestFloatNormalization() { ShaderParameterDefinition definition = MakeFloatDefinition(); ShaderParameterValue value; std::string error; Expect(NormalizeAndValidateParameterValue(definition, JsonValue(2.0), value, error), "float parameter accepts numeric values"); Expect(value.numberValues.size() == 1 && value.numberValues.front() == 1.0, "float parameter clamps to max"); error.clear(); Expect(NormalizeAndValidateParameterValue(definition, JsonValue(-10.0), value, error), "float parameter accepts low numeric values"); Expect(value.numberValues.size() == 1 && value.numberValues.front() == 0.0, "float parameter clamps to min"); error.clear(); Expect(!NormalizeAndValidateParameterValue(definition, JsonValue("bad"), value, error), "float parameter rejects non-numeric values"); Expect(!error.empty(), "float rejection includes an error"); } void TestVectorNormalization() { ShaderParameterDefinition definition; definition.id = "offset"; definition.type = ShaderParameterType::Vec2; definition.defaultNumbers = { 0.0, 0.0 }; definition.minNumbers = { -1.0, -2.0 }; definition.maxNumbers = { 1.0, 2.0 }; JsonValue input = JsonValue::MakeArray(); input.pushBack(JsonValue(5.0)); input.pushBack(JsonValue(-5.0)); ShaderParameterValue value; std::string error; Expect(NormalizeAndValidateParameterValue(definition, input, value, error), "vec2 parameter accepts arrays"); Expect(value.numberValues.size() == 2 && value.numberValues[0] == 1.0 && value.numberValues[1] == -2.0, "vec2 parameter clamps each component"); JsonValue shortInput = JsonValue::MakeArray(); shortInput.pushBack(JsonValue(0.0)); error.clear(); Expect(!NormalizeAndValidateParameterValue(definition, shortInput, value, error), "vec2 parameter rejects wrong component count"); } void TestColorAndBooleanNormalization() { ShaderParameterDefinition colorDefinition; colorDefinition.id = "tint"; colorDefinition.type = ShaderParameterType::Color; colorDefinition.defaultNumbers = { 1.0, 1.0, 1.0, 1.0 }; colorDefinition.minNumbers = { 0.0, 0.0, 0.0, 0.0 }; colorDefinition.maxNumbers = { 1.0, 1.0, 1.0, 1.0 }; JsonValue colorInput = JsonValue::MakeArray(); colorInput.pushBack(JsonValue(-0.5)); colorInput.pushBack(JsonValue(0.25)); colorInput.pushBack(JsonValue(1.5)); colorInput.pushBack(JsonValue(0.75)); ShaderParameterValue value; std::string error; Expect(NormalizeAndValidateParameterValue(colorDefinition, colorInput, value, error), "color parameter accepts four-component arrays"); Expect(value.numberValues.size() == 4 && value.numberValues[0] == 0.0 && value.numberValues[1] == 0.25 && value.numberValues[2] == 1.0 && value.numberValues[3] == 0.75, "color parameter clamps each component"); JsonValue shortColor = JsonValue::MakeArray(); shortColor.pushBack(JsonValue(1.0)); shortColor.pushBack(JsonValue(1.0)); shortColor.pushBack(JsonValue(1.0)); error.clear(); Expect(!NormalizeAndValidateParameterValue(colorDefinition, shortColor, value, error), "color parameter rejects wrong component count"); ShaderParameterDefinition boolDefinition; boolDefinition.id = "enabled"; boolDefinition.type = ShaderParameterType::Boolean; boolDefinition.defaultBoolean = true; ShaderParameterValue defaultValue = DefaultValueForDefinition(boolDefinition); Expect(defaultValue.booleanValue, "boolean default is copied from definition"); error.clear(); Expect(NormalizeAndValidateParameterValue(boolDefinition, JsonValue(false), value, error), "boolean parameter accepts boolean values"); Expect(!value.booleanValue, "boolean parameter stores selected value"); error.clear(); Expect(!NormalizeAndValidateParameterValue(boolDefinition, JsonValue("false"), value, error), "boolean parameter rejects string values"); } void TestEnumAndDefaults() { ShaderParameterDefinition definition; definition.id = "mode"; definition.type = ShaderParameterType::Enum; definition.defaultEnumValue = "soft"; definition.enumOptions = { { "soft", "Soft" }, { "hard", "Hard" } }; ShaderParameterValue defaultValue = DefaultValueForDefinition(definition); Expect(defaultValue.enumValue == "soft", "enum default is copied from definition"); ShaderParameterValue value; std::string error; Expect(NormalizeAndValidateParameterValue(definition, JsonValue("hard"), value, error), "enum accepts listed options"); Expect(value.enumValue == "hard", "enum stores selected option"); error.clear(); Expect(!NormalizeAndValidateParameterValue(definition, JsonValue("other"), value, error), "enum rejects unknown options"); } void TestTextNormalization() { ShaderParameterDefinition definition; definition.id = "titleText"; definition.type = ShaderParameterType::Text; definition.defaultTextValue = "DEFAULT"; definition.maxLength = 6; ShaderParameterValue defaultValue = DefaultValueForDefinition(definition); Expect(defaultValue.textValue == "DEFAUL", "text default is clamped to max length"); ShaderParameterValue value; std::string error; Expect(NormalizeAndValidateParameterValue(definition, JsonValue("ABC\tDEF\x01GHI"), value, error), "text accepts string values"); Expect(value.textValue == "ABCDEF", "text drops non-printable characters and clamps length"); error.clear(); Expect(!NormalizeAndValidateParameterValue(definition, JsonValue(12.0), value, error), "text rejects non-string values"); } void TestTriggerNormalization() { ShaderParameterDefinition definition; definition.id = "burst"; definition.type = ShaderParameterType::Trigger; ShaderParameterValue defaultValue = DefaultValueForDefinition(definition); Expect(defaultValue.numberValues.size() == 2 && defaultValue.numberValues[0] == 0.0, "trigger defaults to an unfired count"); Expect(defaultValue.numberValues[1] < 0.0, "trigger default time is safely in the past"); ShaderParameterValue value; std::string error; Expect(NormalizeAndValidateParameterValue(definition, JsonValue(4.8), value, error), "trigger accepts numeric counts"); Expect(value.numberValues.size() == 2 && value.numberValues[0] == 4.0, "trigger count is floored to an integer"); error.clear(); Expect(NormalizeAndValidateParameterValue(definition, JsonValue(true), value, error), "trigger accepts boolean pulse values"); error.clear(); Expect(!NormalizeAndValidateParameterValue(definition, JsonValue("fire"), value, error), "trigger rejects string values"); } } int main() { TestSafePresetFileStems(); TestFloatNormalization(); TestVectorNormalization(); TestColorAndBooleanNormalization(); TestEnumAndDefaults(); TestTextNormalization(); TestTriggerNormalization(); if (gFailures != 0) { std::cerr << gFailures << " RuntimeParameterUtils test failure(s).\n"; return 1; } std::cout << "RuntimeParameterUtils tests passed.\n"; return 0; }