first commit
This commit is contained in:
202
Nitrox.Test/Helper/Faker/NitroxCollectionFaker.cs
Normal file
202
Nitrox.Test/Helper/Faker/NitroxCollectionFaker.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using NitroxModel.DataStructures;
|
||||
|
||||
namespace Nitrox.Test.Helper.Faker;
|
||||
|
||||
public class NitroxCollectionFaker : NitroxFaker, INitroxFaker
|
||||
{
|
||||
public enum CollectionType
|
||||
{
|
||||
NONE,
|
||||
ARRAY,
|
||||
LIST,
|
||||
DICTIONARY,
|
||||
SET,
|
||||
QUEUE
|
||||
}
|
||||
|
||||
private const int DEFAULT_SIZE = 2;
|
||||
|
||||
public static bool IsCollection(Type t, out CollectionType collectionType)
|
||||
{
|
||||
if (t.IsArray && t.GetArrayRank() == 1)
|
||||
{
|
||||
collectionType = CollectionType.ARRAY;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
Type[] genericInterfacesDefinition = t.GetInterfaces()
|
||||
.Where(i => i.IsGenericType)
|
||||
.Select(i => i.GetGenericTypeDefinition())
|
||||
.ToArray();
|
||||
|
||||
if (genericInterfacesDefinition.Any(i => i == typeof(IList<>)))
|
||||
{
|
||||
collectionType = CollectionType.LIST;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (genericInterfacesDefinition.Any(i => i == typeof(IDictionary<,>)))
|
||||
{
|
||||
collectionType = CollectionType.DICTIONARY;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (genericInterfacesDefinition.Any(i => i == typeof(ISet<>)))
|
||||
{
|
||||
collectionType = CollectionType.SET;
|
||||
return true;
|
||||
}
|
||||
|
||||
Type genericTypeDefinition = t.GetGenericTypeDefinition();
|
||||
if (genericTypeDefinition == typeof(Queue<>) || genericTypeDefinition == typeof(ThreadSafeQueue<>)) // Queue has no defining interface
|
||||
{
|
||||
collectionType = CollectionType.QUEUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
collectionType = CollectionType.NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryGetCollectionTypes(Type type, out Type[] types)
|
||||
{
|
||||
if (!IsCollection(type, out CollectionType collectionType))
|
||||
{
|
||||
types = [];
|
||||
return false;
|
||||
}
|
||||
|
||||
if (collectionType == CollectionType.ARRAY)
|
||||
{
|
||||
types = [type.GetElementType()];
|
||||
return true;
|
||||
}
|
||||
|
||||
types = type.GenericTypeArguments;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GenerateSize = DEFAULT_SIZE;
|
||||
public Type OutputCollectionType;
|
||||
|
||||
private readonly INitroxFaker[] subFakers;
|
||||
private readonly Func<HashSet<Type>, object> generateAction;
|
||||
|
||||
public NitroxCollectionFaker(Type type, CollectionType collectionType)
|
||||
{
|
||||
OutputCollectionType = type;
|
||||
INitroxFaker elementFaker;
|
||||
|
||||
switch (collectionType)
|
||||
{
|
||||
case CollectionType.ARRAY:
|
||||
Type arrayType = OutputType = type.GetElementType();
|
||||
elementFaker = GetOrCreateFaker(arrayType);
|
||||
subFakers = [elementFaker];
|
||||
|
||||
generateAction = typeTree =>
|
||||
{
|
||||
typeTree.Add(arrayType);
|
||||
Array array = Array.CreateInstance(arrayType, GenerateSize);
|
||||
|
||||
for (int i = 0; i < GenerateSize; i++)
|
||||
{
|
||||
array.SetValue(elementFaker.GenerateUnsafe(typeTree), i);
|
||||
}
|
||||
|
||||
typeTree.Remove(arrayType);
|
||||
return array;
|
||||
};
|
||||
break;
|
||||
case CollectionType.LIST:
|
||||
case CollectionType.SET:
|
||||
Type listType = OutputType = type.GenericTypeArguments[0];
|
||||
elementFaker = GetOrCreateFaker(listType);
|
||||
subFakers = [elementFaker];
|
||||
|
||||
generateAction = typeTree =>
|
||||
{
|
||||
typeTree.Add(listType);
|
||||
dynamic list = Activator.CreateInstance(type);
|
||||
|
||||
for (int i = 0; i < GenerateSize; i++)
|
||||
{
|
||||
MethodInfo castMethod = CastMethodBase.MakeGenericMethod(OutputType);
|
||||
dynamic castedObject = castMethod.Invoke(null, [elementFaker.GenerateUnsafe(typeTree)]);
|
||||
list.Add(castedObject);
|
||||
}
|
||||
|
||||
typeTree.Remove(listType);
|
||||
return list;
|
||||
};
|
||||
break;
|
||||
case CollectionType.DICTIONARY:
|
||||
Type[] dicType = type.GenericTypeArguments;
|
||||
OutputType = dicType[1]; // A little hacky but should work as we don't use circular dependencies as keys
|
||||
INitroxFaker keyFaker = GetOrCreateFaker(dicType[0]);
|
||||
INitroxFaker valueFaker = GetOrCreateFaker(dicType[1]);
|
||||
subFakers = [keyFaker, valueFaker];
|
||||
|
||||
generateAction = typeTree =>
|
||||
{
|
||||
typeTree.Add(dicType[0]);
|
||||
typeTree.Add(dicType[1]);
|
||||
IDictionary dict = (IDictionary)Activator.CreateInstance(type);
|
||||
for (int i = 0; i < GenerateSize; i++)
|
||||
{
|
||||
for (int tries = 0; tries < 10; tries++)
|
||||
{
|
||||
object key = keyFaker.GenerateUnsafe(typeTree);
|
||||
|
||||
if (!dict.Contains(key))
|
||||
{
|
||||
dict.Add(key, valueFaker.GenerateUnsafe(typeTree));
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries == 9)
|
||||
{
|
||||
throw new InvalidOperationException($"While generating action for filling Dictionary an unique key of {dicType[0]} couldn't be generated even after 10 tries");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typeTree.Remove(dicType[0]);
|
||||
typeTree.Remove(dicType[1]);
|
||||
return dict;
|
||||
};
|
||||
break;
|
||||
case CollectionType.QUEUE:
|
||||
Type queueType = OutputType = type.GenericTypeArguments[0];
|
||||
elementFaker = GetOrCreateFaker(queueType);
|
||||
subFakers = [elementFaker];
|
||||
|
||||
generateAction = typeTree =>
|
||||
{
|
||||
typeTree.Add(queueType);
|
||||
dynamic queue = Activator.CreateInstance(type);
|
||||
|
||||
for (int i = 0; i < GenerateSize; i++)
|
||||
{
|
||||
MethodInfo castMethod = CastMethodBase.MakeGenericMethod(OutputType);
|
||||
dynamic castedObject = castMethod.Invoke(null, [elementFaker.GenerateUnsafe(typeTree)]);
|
||||
queue!.Enqueue(castedObject);
|
||||
}
|
||||
|
||||
typeTree.Remove(queueType);
|
||||
return queue;
|
||||
};
|
||||
break;
|
||||
case CollectionType.NONE:
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(collectionType), collectionType, null);
|
||||
}
|
||||
}
|
||||
|
||||
public INitroxFaker[] GetSubFakers() => subFakers;
|
||||
|
||||
public object GenerateUnsafe(HashSet<Type> typeTree) => generateAction.Invoke(typeTree);
|
||||
}
|
Reference in New Issue
Block a user