TestsAllViews.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #nullable enable
  2. using System.Reflection;
  3. namespace UnitTests;
  4. /// <summary>
  5. /// Base class for tests that need to test all views.
  6. /// </summary>
  7. public class TestsAllViews : FakeDriverBase
  8. {
  9. /// <summary>
  10. /// Gets all view types.
  11. /// </summary>
  12. public static IEnumerable<object []> AllViewTypes =>
  13. typeof (View).Assembly
  14. .GetTypes ()
  15. .Where (type => type is { IsClass: true, IsAbstract: false, IsPublic: true }
  16. && (type.IsSubclassOf (typeof (View)) || type == typeof (View)))
  17. .Select (type => new object [] { type });
  18. /// <summary>
  19. /// Creates an instance of a view if it is not a generic type.
  20. /// </summary>
  21. /// <param name="type"></param>
  22. /// <returns></returns>
  23. public static View? CreateInstanceIfNotGeneric (Type type)
  24. {
  25. if (type.IsGenericType)
  26. {
  27. // Return null for generic types
  28. return null;
  29. }
  30. return Activator.CreateInstance (type) as View;
  31. }
  32. /// <summary>
  33. /// Gets a list of all view classes.
  34. /// </summary>
  35. /// <returns></returns>
  36. public static List<Type> GetAllViewClasses ()
  37. {
  38. return typeof (View).Assembly.GetTypes ()
  39. .Where (myType => myType is { IsClass: true, IsAbstract: false, IsPublic: true }
  40. && myType.IsSubclassOf (typeof (View))
  41. )
  42. .ToList ();
  43. }
  44. /// <summary>
  45. /// Creates a view from a type.
  46. /// </summary>
  47. /// <param name="type">The type.</param>
  48. /// <param name="ctor">The constructor to call.</param>
  49. /// <returns></returns>
  50. public static View? CreateViewFromType (Type type, ConstructorInfo ctor)
  51. {
  52. View? viewType = null;
  53. if (type is { IsGenericType: true, IsTypeDefinition: true })
  54. {
  55. List<Type> typeArguments = new ();
  56. // use <object> or the original type if applicable
  57. foreach (Type arg in type.GetGenericArguments ())
  58. {
  59. // Check if this type parameter has constraints that object can't satisfy
  60. Type [] constraints = arg.GetGenericParameterConstraints ();
  61. // If there's a View constraint, use View instead of object
  62. if (constraints.Any (c => c == typeof (View) || c.IsSubclassOf (typeof (View))))
  63. {
  64. typeArguments.Add (typeof (View));
  65. }
  66. else if (arg.IsValueType && Nullable.GetUnderlyingType (arg) == null)
  67. {
  68. typeArguments.Add (arg);
  69. }
  70. else
  71. {
  72. typeArguments.Add (typeof (object));
  73. }
  74. }
  75. type = type.MakeGenericType (typeArguments.ToArray ());
  76. // Ensure the type does not contain any generic parameters
  77. if (type.ContainsGenericParameters)
  78. {
  79. Logging.Warning ($"Cannot create an instance of {type} because it contains generic parameters.");
  80. //throw new ArgumentException ($"Cannot create an instance of {type} because it contains generic parameters.");
  81. return null;
  82. }
  83. // Check if the type has required properties that can't be satisfied by Activator.CreateInstance
  84. // This handles cases like RunnableWrapper which has a required WrappedView property
  85. if (HasRequiredProperties (type))
  86. {
  87. Logging.Warning ($"Cannot create an instance of {type} because it has required properties that must be set.");
  88. return null;
  89. }
  90. Assert.IsType (type, (View)Activator.CreateInstance (type)!);
  91. }
  92. else
  93. {
  94. ParameterInfo [] paramsInfo = ctor.GetParameters ();
  95. Type paramType;
  96. List<object> pTypes = new ();
  97. if (type.IsGenericType)
  98. {
  99. foreach (Type args in type.GetGenericArguments ())
  100. {
  101. paramType = args.GetType ();
  102. if (args.Name == "T")
  103. {
  104. pTypes.Add (typeof (object));
  105. }
  106. else
  107. {
  108. AddArguments (paramType, pTypes);
  109. }
  110. }
  111. }
  112. foreach (ParameterInfo p in paramsInfo)
  113. {
  114. paramType = p.ParameterType;
  115. if (p.HasDefaultValue)
  116. {
  117. pTypes.Add (p.DefaultValue!);
  118. }
  119. else
  120. {
  121. AddArguments (paramType, pTypes);
  122. }
  123. }
  124. if (type is { IsGenericType: true, IsTypeDefinition: false })
  125. {
  126. viewType = Activator.CreateInstance (type) as View;
  127. }
  128. else
  129. {
  130. viewType = (View)ctor.Invoke (pTypes.ToArray ());
  131. }
  132. Assert.IsType (type, viewType);
  133. }
  134. return viewType;
  135. }
  136. /// <summary>
  137. /// Checks if a type has required properties (C# 11 feature).
  138. /// </summary>
  139. private static bool HasRequiredProperties (Type type)
  140. {
  141. // Check all public instance properties for the RequiredMemberAttribute
  142. return type.GetProperties (BindingFlags.Public | BindingFlags.Instance)
  143. .Any (p => p.GetCustomAttributes (typeof (System.Runtime.CompilerServices.RequiredMemberAttribute), true).Any ());
  144. }
  145. private static void AddArguments (Type paramType, List<object> pTypes)
  146. {
  147. if (paramType == typeof (Rectangle))
  148. {
  149. pTypes.Add (Rectangle.Empty);
  150. }
  151. else if (paramType == typeof (string))
  152. {
  153. pTypes.Add (string.Empty);
  154. }
  155. else if (paramType == typeof (int))
  156. {
  157. pTypes.Add (0);
  158. }
  159. else if (paramType == typeof (bool))
  160. {
  161. pTypes.Add (true);
  162. }
  163. else if (paramType.Name == "IList")
  164. {
  165. pTypes.Add (new List<object> ());
  166. }
  167. else if (paramType.Name == "View")
  168. {
  169. var top = new Runnable ();
  170. var view = new View ();
  171. top.Add (view);
  172. pTypes.Add (view);
  173. }
  174. else if (paramType.Name == "View[]")
  175. {
  176. pTypes.Add (new View [] { });
  177. }
  178. else if (paramType.Name == "Stream")
  179. {
  180. pTypes.Add (new MemoryStream ());
  181. }
  182. else if (paramType.Name == "String")
  183. {
  184. pTypes.Add (string.Empty);
  185. }
  186. else if (paramType.Name == "TreeView`1[T]")
  187. {
  188. pTypes.Add (string.Empty);
  189. }
  190. else
  191. {
  192. pTypes.Add (null!);
  193. }
  194. }
  195. }