mirror of
https://github.com/dcronqvist/DotTiled.git
synced 2025-02-05 08:52:50 +02:00
Added some new FromClass methods to construct custom class definitions, tests not done
This commit is contained in:
parent
e285f97825
commit
84b55fe005
3 changed files with 210 additions and 4 deletions
|
@ -5,7 +5,7 @@ namespace DotTiled.Tests;
|
||||||
|
|
||||||
public static partial class DotTiledAssert
|
public static partial class DotTiledAssert
|
||||||
{
|
{
|
||||||
private static void AssertListOrdered<T>(IList<T> expected, IList<T> actual, string nameof, Action<T, T> assertEqual = null)
|
internal static void AssertListOrdered<T>(IList<T> expected, IList<T> actual, string nameof, Action<T, T> assertEqual = null)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ public static partial class DotTiledAssert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertOptionalsEqual<T>(
|
internal static void AssertOptionalsEqual<T>(
|
||||||
Optional<T> expected,
|
Optional<T> expected,
|
||||||
Optional<T> actual,
|
Optional<T> actual,
|
||||||
string nameof,
|
string nameof,
|
||||||
|
@ -49,7 +49,7 @@ public static partial class DotTiledAssert
|
||||||
Assert.False(actual.HasValue, $"Expected {nameof} to not have a value");
|
Assert.False(actual.HasValue, $"Expected {nameof} to not have a value");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertEqual<T>(Optional<T> expected, Optional<T> actual, string nameof)
|
internal static void AssertEqual<T>(Optional<T> expected, Optional<T> actual, string nameof)
|
||||||
{
|
{
|
||||||
if (expected is null)
|
if (expected is null)
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ public static partial class DotTiledAssert
|
||||||
Assert.False(actual.HasValue, $"Expected {nameof} to not have a value");
|
Assert.False(actual.HasValue, $"Expected {nameof} to not have a value");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertEqual<T>(T expected, T actual, string nameof)
|
internal static void AssertEqual<T>(T expected, T actual, string nameof)
|
||||||
{
|
{
|
||||||
if (expected == null)
|
if (expected == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,112 @@
|
||||||
|
namespace DotTiled.Tests;
|
||||||
|
|
||||||
|
public class CustomClassDefinitionTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void FromClassType_WhenTypeIsNotCustomClass_ThrowsArgumentException()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var type = typeof(string);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<ArgumentException>(() => CustomClassDefinition.FromClassType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TestClass1
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassDefinition ExpectedTestClass1Definition => new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass1",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = new List<IProperty>
|
||||||
|
{
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private sealed class TestClass2WithNestedClass
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
public TestClass1 Nested { get; set; } = new TestClass1();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassDefinition ExpectedTestClass2WithNestedClassDefinition => new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass2WithNestedClass",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 },
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Nested",
|
||||||
|
PropertyType = "TestClass1",
|
||||||
|
Value = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
private sealed class TestClass3WithOverridenNestedClass
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "John Doe";
|
||||||
|
public int Age { get; set; } = 42;
|
||||||
|
public TestClass1 Nested { get; set; } = new TestClass1
|
||||||
|
{
|
||||||
|
Name = "Jane Doe"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CustomClassDefinition ExpectedTestClass3WithOverridenNestedClassDefinition => new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = "TestClass3WithOverridenNestedClass",
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = [
|
||||||
|
new StringProperty { Name = "Name", Value = "John Doe" },
|
||||||
|
new IntProperty { Name = "Age", Value = 42 },
|
||||||
|
new ClassProperty
|
||||||
|
{
|
||||||
|
Name = "Nested",
|
||||||
|
PropertyType = "TestClass1",
|
||||||
|
Value = [
|
||||||
|
new StringProperty { Name = "Name", Value = "Jane Doe" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
private static IEnumerable<(Type, CustomClassDefinition)> GetCustomClassDefinitionTestData()
|
||||||
|
{
|
||||||
|
yield return (typeof(TestClass1), ExpectedTestClass1Definition);
|
||||||
|
yield return (typeof(TestClass2WithNestedClass), ExpectedTestClass2WithNestedClassDefinition);
|
||||||
|
yield return (typeof(TestClass3WithOverridenNestedClass), ExpectedTestClass3WithOverridenNestedClassDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssertCustomClassDefinitionEqual(CustomClassDefinition expected, CustomClassDefinition actual)
|
||||||
|
{
|
||||||
|
DotTiledAssert.AssertEqual(expected.ID, actual.ID, nameof(CustomClassDefinition.ID));
|
||||||
|
DotTiledAssert.AssertEqual(expected.Name, actual.Name, nameof(CustomClassDefinition.Name));
|
||||||
|
DotTiledAssert.AssertEqual(expected.Color, actual.Color, nameof(CustomClassDefinition.Color));
|
||||||
|
DotTiledAssert.AssertEqual(expected.DrawFill, actual.DrawFill, nameof(CustomClassDefinition.DrawFill));
|
||||||
|
DotTiledAssert.AssertEqual(expected.UseAs, actual.UseAs, nameof(CustomClassDefinition.UseAs));
|
||||||
|
DotTiledAssert.AssertProperties(expected.Members, actual.Members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> CustomClassDefinitionTestData =>
|
||||||
|
GetCustomClassDefinitionTestData().Select(data => new object[] { data.Item1, data.Item2 });
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(CustomClassDefinitionTestData))]
|
||||||
|
public void FromClassType_WhenTypeIsCustomClass_ReturnsCustomClassDefinition(Type type, CustomClassDefinition expected)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var result = CustomClassDefinition.FromClassType(type);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
AssertCustomClassDefinitionEqual(expected, result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace DotTiled;
|
namespace DotTiled;
|
||||||
|
|
||||||
|
@ -95,4 +97,96 @@ public class CustomClassDefinition : HasPropertiesBase, ICustomTypeDefinition
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override IList<IProperty> GetProperties() => Members;
|
public override IList<IProperty> GetProperties() => Members;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified class type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of the class to create a custom class definition from.</param>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when the specified type is not a class.</exception>
|
||||||
|
public static CustomClassDefinition FromClassType(Type type)
|
||||||
|
{
|
||||||
|
if (type == typeof(string) || !type.IsClass)
|
||||||
|
throw new ArgumentException("Type must be a class.", nameof(type));
|
||||||
|
|
||||||
|
return FromClass(() => Activator.CreateInstance(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified instance of a class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance">The instance of the class to create a custom class definition from.</param>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
public static CustomClassDefinition FromClassInstance(dynamic instance)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(instance);
|
||||||
|
return FromClass(() => instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified constructible class type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the class to create a custom class definition from.</typeparam>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
public static CustomClassDefinition FromClass<T>() where T : class, new() => FromClass(() => new T());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="CustomClassDefinition"/> from the specified factory function of a class instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the class to create a custom class definition from.</typeparam>
|
||||||
|
/// <param name="factory">The factory function that creates an instance of the class.</param>
|
||||||
|
/// <returns>A new <see cref="CustomClassDefinition"/> instance.</returns>
|
||||||
|
public static CustomClassDefinition FromClass<T>(Func<T> factory) where T : class
|
||||||
|
{
|
||||||
|
var instance = factory();
|
||||||
|
var type = instance.GetType();
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
|
||||||
|
return new CustomClassDefinition
|
||||||
|
{
|
||||||
|
Name = type.Name,
|
||||||
|
UseAs = CustomClassUseAs.All,
|
||||||
|
Members = properties.Select(p => ConvertPropertyInfoToIProperty(instance, p)).ToList()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IProperty ConvertPropertyInfoToIProperty(object instance, PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
switch (propertyInfo.PropertyType)
|
||||||
|
{
|
||||||
|
case Type t when t == typeof(bool):
|
||||||
|
return new BoolProperty { Name = propertyInfo.Name, Value = (bool)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(Color):
|
||||||
|
return new ColorProperty { Name = propertyInfo.Name, Value = (Color)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(float):
|
||||||
|
return new FloatProperty { Name = propertyInfo.Name, Value = (float)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(string):
|
||||||
|
return new StringProperty { Name = propertyInfo.Name, Value = (string)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t == typeof(int):
|
||||||
|
return new IntProperty { Name = propertyInfo.Name, Value = (int)propertyInfo.GetValue(instance) };
|
||||||
|
case Type t when t.IsClass:
|
||||||
|
return new ClassProperty { Name = propertyInfo.Name, PropertyType = t.Name, Value = GetNestedProperties(propertyInfo.PropertyType, propertyInfo.GetValue(instance)) };
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException($"Type '{propertyInfo.PropertyType.Name}' is not supported in custom classes.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<IProperty> GetNestedProperties(Type type, object instance)
|
||||||
|
{
|
||||||
|
var defaultInstance = Activator.CreateInstance(type);
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
|
||||||
|
bool IsPropertyDefaultValue(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
var defaultValue = propertyInfo.GetValue(defaultInstance);
|
||||||
|
var value = propertyInfo.GetValue(instance);
|
||||||
|
return value.Equals(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties
|
||||||
|
.Where(p => !IsPropertyDefaultValue(p))
|
||||||
|
.Select(p => ConvertPropertyInfoToIProperty(instance, p)).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue