Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ protected static bool TryGetConfiguredValue<TValue>(object? dataTypeConfiguratio
return false;
}

if (configuration.TryGetValue(key, out object? obj) && obj is TValue castValue)
if (configuration.TryGetValue(key, out object? obj) && TryCastValue(obj, out TValue? castValue))
{
value = castValue;
return true;
Expand All @@ -30,4 +30,24 @@ protected static bool TryGetConfiguredValue<TValue>(object? dataTypeConfiguratio
value = default;
return false;
}

private static bool TryCastValue<TValue>(object? value, [NotNullWhen(true)] out TValue? castValue)
{
if (value is TValue valueAsType)
{
castValue = valueAsType;
return true;
}

// Special case for floating point numbers - when deserialized these will be integers if whole numbers rather
// than double.
if (typeof(TValue) == typeof(double) && value is int valueAsInt)
{
castValue = (TValue)(object)Convert.ToDouble(valueAsInt);
return true;
}

castValue = default;
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,25 @@ public void Validates_Is_Less_Than_Or_Equal_To_Configured_Max(object value, bool
}
}

[TestCase(1.8, true)]
[TestCase(2.2, false)]
public void Validates_Is_Less_Than_Or_Equal_To_Configured_Max_With_Configured_Whole_Numbers(object value, bool expectedSuccess)
{
var editor = CreateValueEditor(min: 1, max: 2);
var result = editor.Validate(value, false, null, PropertyValidationContext.Empty());
if (expectedSuccess)
{
Assert.IsEmpty(result);
}
else
{
Assert.AreEqual(1, result.Count());

var validationResult = result.First();
Assert.AreEqual(validationResult.ErrorMessage, "validation_outOfRangeMaximum");
}
}

[TestCase(0.2, 1.4, false)]
[TestCase(0.2, 1.5, true)]
[TestCase(0.0, 1.4, true)] // A step of zero would trigger a divide by zero error in evaluating. So we always pass validation for zero, as effectively any step value is valid.
Expand Down Expand Up @@ -165,7 +184,7 @@ public void Validates_Matches_Configured_Step(double step, object value, bool ex
return CreateValueEditor().ToEditor(property.Object);
}

private static DecimalPropertyEditor.DecimalPropertyValueEditor CreateValueEditor(double step = 0.2)
private static DecimalPropertyEditor.DecimalPropertyValueEditor CreateValueEditor(double min = 1.1, double max = 1.9, double step = 0.2)
{
var localizedTextServiceMock = new Mock<ILocalizedTextService>();
localizedTextServiceMock.Setup(x => x.Localize(
Expand All @@ -174,19 +193,45 @@ private static DecimalPropertyEditor.DecimalPropertyValueEditor CreateValueEdito
It.IsAny<CultureInfo>(),
It.IsAny<IDictionary<string, string>>()))
.Returns((string key, string alias, CultureInfo culture, IDictionary<string, string> args) => $"{key}_{alias}");

// When configuration is populated from the deserialized JSON, whole number values are deserialized as integers.
// So we want to replicate that in our tests.
var configuration = new Dictionary<string, object>();
if (min % 1 == 0)
{
configuration.Add("min", (int)min);
}
else
{
configuration.Add("min", min);
}

if (max % 1 == 0)
{
configuration.Add("max", (int)max);
}
else
{
configuration.Add("max", max);
}

if (step % 1 == 0)
{
configuration.Add("step", (int)step);
}
else
{
configuration.Add("step", step);
}

return new DecimalPropertyEditor.DecimalPropertyValueEditor(
Mock.Of<IShortStringHelper>(),
Mock.Of<IJsonSerializer>(),
Mock.Of<IIOHelper>(),
new DataEditorAttribute("alias"),
localizedTextServiceMock.Object)
{
ConfigurationObject = new Dictionary<string, object>
{
{ "min", 1.1 },
{ "max", 1.9 },
{ "step", step }
}
ConfigurationObject = configuration
};
}
}