#nullable enable
using System.Reflection;
namespace UnitTests;
///
/// Base class for tests that need to test all views.
///
public class TestsAllViews : FakeDriverBase
{
///
/// Gets all view types.
///
public static IEnumerable AllViewTypes =>
typeof (View).Assembly
.GetTypes ()
.Where (type => type is { IsClass: true, IsAbstract: false, IsPublic: true }
&& (type.IsSubclassOf (typeof (View)) || type == typeof (View)))
.Select (type => new object [] { type });
///
/// Creates an instance of a view if it is not a generic type.
///
///
///
public static View? CreateInstanceIfNotGeneric (Type type)
{
if (type.IsGenericType)
{
// Return null for generic types
return null;
}
return Activator.CreateInstance (type) as View;
}
///
/// Gets a list of all view classes.
///
///
public static List GetAllViewClasses ()
{
return typeof (View).Assembly.GetTypes ()
.Where (myType => myType is { IsClass: true, IsAbstract: false, IsPublic: true }
&& myType.IsSubclassOf (typeof (View))
)
.ToList ();
}
///
/// Creates a view from a type.
///
/// The type.
/// The constructor to call.
///
public static View? CreateViewFromType (Type type, ConstructorInfo ctor)
{
View? viewType = null;
if (type is { IsGenericType: true, IsTypeDefinition: true })
{
List typeArguments = new ();
// use or the original type if applicable
foreach (Type arg in type.GetGenericArguments ())
{
// Check if this type parameter has constraints that object can't satisfy
Type [] constraints = arg.GetGenericParameterConstraints ();
// If there's a View constraint, use View instead of object
if (constraints.Any (c => c == typeof (View) || c.IsSubclassOf (typeof (View))))
{
typeArguments.Add (typeof (View));
}
else if (arg.IsValueType && Nullable.GetUnderlyingType (arg) == null)
{
typeArguments.Add (arg);
}
else
{
typeArguments.Add (typeof (object));
}
}
type = type.MakeGenericType (typeArguments.ToArray ());
// Ensure the type does not contain any generic parameters
if (type.ContainsGenericParameters)
{
Logging.Warning ($"Cannot create an instance of {type} because it contains generic parameters.");
//throw new ArgumentException ($"Cannot create an instance of {type} because it contains generic parameters.");
return null;
}
// Check if the type has required properties that can't be satisfied by Activator.CreateInstance
// This handles cases like RunnableWrapper which has a required WrappedView property
if (HasRequiredProperties (type))
{
Logging.Warning ($"Cannot create an instance of {type} because it has required properties that must be set.");
return null;
}
Assert.IsType (type, (View)Activator.CreateInstance (type)!);
}
else
{
ParameterInfo [] paramsInfo = ctor.GetParameters ();
Type paramType;
List pTypes = new ();
if (type.IsGenericType)
{
foreach (Type args in type.GetGenericArguments ())
{
paramType = args.GetType ();
if (args.Name == "T")
{
pTypes.Add (typeof (object));
}
else
{
AddArguments (paramType, pTypes);
}
}
}
foreach (ParameterInfo p in paramsInfo)
{
paramType = p.ParameterType;
if (p.HasDefaultValue)
{
pTypes.Add (p.DefaultValue!);
}
else
{
AddArguments (paramType, pTypes);
}
}
if (type is { IsGenericType: true, IsTypeDefinition: false })
{
viewType = Activator.CreateInstance (type) as View;
}
else
{
viewType = (View)ctor.Invoke (pTypes.ToArray ());
}
Assert.IsType (type, viewType);
}
return viewType;
}
///
/// Checks if a type has required properties (C# 11 feature).
///
private static bool HasRequiredProperties (Type type)
{
// Check all public instance properties for the RequiredMemberAttribute
return type.GetProperties (BindingFlags.Public | BindingFlags.Instance)
.Any (p => p.GetCustomAttributes (typeof (System.Runtime.CompilerServices.RequiredMemberAttribute), true).Any ());
}
private static void AddArguments (Type paramType, List pTypes)
{
if (paramType == typeof (Rectangle))
{
pTypes.Add (Rectangle.Empty);
}
else if (paramType == typeof (string))
{
pTypes.Add (string.Empty);
}
else if (paramType == typeof (int))
{
pTypes.Add (0);
}
else if (paramType == typeof (bool))
{
pTypes.Add (true);
}
else if (paramType.Name == "IList")
{
pTypes.Add (new List ());
}
else if (paramType.Name == "View")
{
var top = new Runnable ();
var view = new View ();
top.Add (view);
pTypes.Add (view);
}
else if (paramType.Name == "View[]")
{
pTypes.Add (new View [] { });
}
else if (paramType.Name == "Stream")
{
pTypes.Add (new MemoryStream ());
}
else if (paramType.Name == "String")
{
pTypes.Add (string.Empty);
}
else if (paramType.Name == "TreeView`1[T]")
{
pTypes.Add (string.Empty);
}
else
{
pTypes.Add (null!);
}
}
}