2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / nunit20 / core / Reflect.cs
1 using System;
2 using System.Reflection;
3 using System.Collections;
4 using NUnit.Framework;
5
6 namespace NUnit.Core
7 {
8         /// <summary>
9         /// Helper methods for inspecting a type by reflection.
10         /// 
11         /// Many of these methods take a MemberInfo as an argument to avoid
12         /// duplication, even though certain attributes can only appear on
13         /// specific types of members, like MethodInfo or Type.
14         /// 
15         /// Generally, these methods perform simple utility functions like
16         /// checking for a given attribute. However, some of the methdods
17         /// actually implement policies, which might change at some later
18         /// time. The intent is that policies that may vary among different
19         /// types of test cases or suites should be handled by those types,
20         /// while common decisions are handled here.
21         /// </summary>
22         public class Reflect
23         {
24                 #region Attribute types used by reflect
25  
26                 public static readonly Type TestFixtureType = typeof( TestFixtureAttribute );
27                 public static readonly Type TestType = typeof( TestAttribute );
28                 public static readonly Type SetUpType = typeof( SetUpAttribute );
29                 public static readonly Type TearDownType = typeof( TearDownAttribute );
30                 public static readonly Type FixtureSetUpType = typeof( TestFixtureSetUpAttribute );
31                 public static readonly Type FixtureTearDownType = typeof( TestFixtureTearDownAttribute );
32                 public static readonly Type ExplicitType = typeof( ExplicitAttribute );
33                 public static readonly Type CategoryType = typeof( CategoryAttribute );
34                 public static readonly Type IgnoreType = typeof( IgnoreAttribute );
35                 public static readonly Type ExpectedExceptionType = typeof( ExpectedExceptionAttribute );
36                 public static readonly Type SuiteType = typeof( SuiteAttribute );
37                 
38                 #endregion
39
40                 #region Binding flags used by reflect
41
42                 private static readonly BindingFlags InstanceMethods =
43                         BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
44
45                 private static readonly BindingFlags StaticMethods =
46                         BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly;
47
48                 private static readonly BindingFlags AllMethods = 
49                         BindingFlags.Public |  BindingFlags.NonPublic |
50                         BindingFlags.Instance | BindingFlags.Static;
51
52                 private static readonly BindingFlags AllDeclaredMethods = 
53                         AllMethods | BindingFlags.DeclaredOnly; 
54
55                 #endregion
56
57                 #region Check for presence of an attribute
58
59                 public static bool HasTestFixtureAttribute(Type type)
60                 {
61                         return type.IsDefined( TestFixtureType, true ); // Inheritable
62                 }
63
64                 public static bool HasTestAttribute(MethodInfo method)
65                 {
66                         return method.IsDefined( TestType, false );
67                 }
68                 
69                 public static bool HasExplicitAttribute(MemberInfo member)
70                 {
71                         return member.IsDefined( ExplicitType, false );
72                 }
73
74                 public static bool HasCategoryAttribute(MemberInfo member) 
75                 {
76                         return member.IsDefined( CategoryType, false );
77                 }
78
79                 public static bool HasExpectedExceptionAttribute(MethodInfo method)
80                 {
81                         return method.IsDefined( ExpectedExceptionType, false );
82                 }
83
84                 public static bool HasIgnoreAttribute( MemberInfo member )
85                 {
86                         return member.IsDefined( IgnoreType, false );
87                 }
88
89                 public static bool HasSuiteAttribute( PropertyInfo property )
90                 {
91                         return property.IsDefined( SuiteType, false );
92                 }
93
94                 #endregion
95
96                 #region Legacy Checks on Names
97
98                 public static bool IsObsoleteTestMethod(MethodInfo methodToCheck)
99                 {
100                         if ( methodToCheck.Name.ToLower().StartsWith("test") )
101                         {
102                                 object[] attributes = methodToCheck.GetCustomAttributes( false );
103
104                                 foreach( Attribute attribute in attributes )
105                                         if( attribute is SetUpAttribute ||
106                                                 attribute is TestFixtureSetUpAttribute ||
107                                                 attribute is TearDownAttribute || 
108                                                 attribute is TestFixtureTearDownAttribute )
109                                         {
110                                                 return false;
111                                         }
112
113                                 return true;    
114                         }
115
116                         return false;
117                 }
118
119                 #endregion
120
121                 #region Get Attributes 
122
123                 public static TestFixtureAttribute GetTestFixtureAttribute( Type type )
124                 {
125                         object[] attributes = type.GetCustomAttributes( TestFixtureType, true );
126                         return attributes.Length > 0 ? (TestFixtureAttribute) attributes[0] : null;
127                 }
128
129                 public static TestAttribute GetTestAttribute( MemberInfo member )
130                 {
131                         object[] attributes = member.GetCustomAttributes( TestType, false );
132                         return attributes.Length > 0 ? (TestAttribute)attributes[0] : null;
133                 }
134
135                 public static IgnoreAttribute GetIgnoreAttribute( MemberInfo member )
136                 {
137                         object[] attributes = member.GetCustomAttributes( IgnoreType, false );
138                         return attributes.Length > 0 ? (IgnoreAttribute) attributes[0] : null;
139                 }
140
141                 public static ExpectedExceptionAttribute GetExpectedExceptionAttribute( MethodInfo method )
142                 {
143                         object[] attributes = method.GetCustomAttributes( ExpectedExceptionType, false);
144                         return attributes.Length > 0 ? (ExpectedExceptionAttribute) attributes[0] : null;
145                 }
146
147                 #endregion
148
149                 #region Get Properties of Attributes
150
151                 public static string GetIgnoreReason( MemberInfo member )
152                 {
153                         IgnoreAttribute attribute = GetIgnoreAttribute( member );
154                         return attribute == null ? "no reason" : attribute.Reason;
155                 }
156
157                 public static string GetDescription( MethodInfo method )
158                 {
159                         TestAttribute attribute = GetTestAttribute( method );
160                         return attribute == null ? null : attribute.Description;
161                 }
162
163                 public static string GetDescription( Type fixtureType )
164                 {
165                         TestFixtureAttribute attribute = GetTestFixtureAttribute( fixtureType );
166                         return attribute == null ? null : attribute.Description;
167                 }
168
169                 #endregion
170
171                 #region Methods to check validity of a type and its members
172
173                 /// <summary>
174                 /// Method to validate that a type is a valid test fixture
175                 /// </summary>
176                 /// <param name="fixtureType">The type to be checked</param>
177                 public static void CheckFixtureType( Type fixtureType )
178                 {
179                         if ( fixtureType.GetConstructor( Type.EmptyTypes ) == null )
180                                 throw new InvalidTestFixtureException(fixtureType.FullName + " does not have a valid constructor");
181                         
182                         CheckSetUpTearDownMethod( fixtureType, SetUpType );
183                         CheckSetUpTearDownMethod( fixtureType, TearDownType );
184                         CheckSetUpTearDownMethod( fixtureType, FixtureSetUpType );
185                         CheckSetUpTearDownMethod( fixtureType, FixtureTearDownType );
186                 }
187
188                 /// <summary>
189                 /// This method verifies that a type has no more than one method of a particular
190                 /// SetUp or TearDown type and that the method has a correct signature.
191                 /// </summary>
192                 /// <param name="fixtureType">The type to be checked</param>
193                 /// <param name="attributeType">The attribute to check for</param>
194                 private static void CheckSetUpTearDownMethod( Type fixtureType, Type attributeType )
195                 {
196                         int count = 0;
197                         MethodInfo theMethod = null;
198
199                         foreach(MethodInfo method in fixtureType.GetMethods( AllDeclaredMethods ))
200                         {
201                                 if( method.IsDefined( attributeType, false ) )
202                                 {
203                                         theMethod = method;
204                                         count++;
205                                 }
206                         }
207
208                         if ( count > 1 )
209                         {
210                                 string attributeName = attributeType.Name;
211                                 if ( attributeName.EndsWith( "Attribute" ) )
212                                         attributeName = attributeName.Substring( 
213                                                 0, attributeName.Length - 9 );
214
215                                 throw new InvalidTestFixtureException( 
216                                         string.Format( "{0} has multiple {1} methods",
217                                         fixtureType.Name, attributeName ) );
218                         }
219
220                         CheckSetUpTearDownSignature( theMethod );
221                 } 
222
223                 private static void CheckSetUpTearDownSignature( MethodInfo method )
224                 {
225                         if ( method != null )
226                         {
227                                 if ( !method.IsPublic && !method.IsFamily || method.IsStatic || method.ReturnType != typeof(void) || method.GetParameters().Length > 0 )
228                                         throw new InvalidTestFixtureException("Invalid SetUp or TearDown method signature");
229                         }
230                 }
231
232                 /// <summary>
233                 /// Check the signature of a test method
234                 /// </summary>
235                 /// <param name="methodToCheck">The method signature to check</param>
236                 /// <returns>True if the signature is correct, otherwise false</returns>
237                 public static bool IsTestMethodSignatureCorrect(MethodInfo methodToCheck)
238                 {
239                         return 
240                                 !methodToCheck.IsStatic
241                                 && !methodToCheck.IsAbstract
242                                 && methodToCheck.IsPublic
243                                 && methodToCheck.GetParameters().Length == 0
244                                 && methodToCheck.ReturnType.Equals(typeof(void));
245                 }
246                 
247                 #endregion
248
249                 #region Get Methods of a type
250
251                 // These methods all take an object and assume that the type of the
252                 // object was pre-checked so that there are no duplicate methods,
253                 // statics, private methods, etc.
254
255                 public static ConstructorInfo GetConstructor( Type fixtureType )
256                 {
257                         return fixtureType.GetConstructor( Type.EmptyTypes );
258                 }
259
260                 public static MethodInfo GetSetUpMethod( Type fixtureType )
261                 {
262                         return GetMethod( fixtureType, SetUpType );
263                 }
264
265                 public static MethodInfo GetTearDownMethod(Type fixtureType)
266                 {                       
267                         return GetMethod(fixtureType, TearDownType );
268                 }
269
270                 public static MethodInfo GetFixtureSetUpMethod( Type fixtureType )
271                 {
272                         return GetMethod( fixtureType, FixtureSetUpType );
273                 }
274
275                 public static MethodInfo GetFixtureTearDownMethod( Type fixtureType )
276                 {
277                         return GetMethod( fixtureType, FixtureTearDownType );
278                 }
279
280                 public static MethodInfo GetMethod( Type fixtureType, Type attributeType )
281                 {
282                         foreach(MethodInfo method in fixtureType.GetMethods( InstanceMethods ) )
283                         {
284                                 if( method.IsDefined( attributeType, true ) ) 
285                                         return method;
286                         }
287
288                         return null;
289                 }
290
291                 public static MethodInfo GetMethod( Type fixtureType, string methodName )
292                 {
293                         foreach(MethodInfo method in fixtureType.GetMethods( InstanceMethods ) )
294                         {
295                                 if( method.Name == methodName ) 
296                                         return method;
297                         }
298
299                         return null;
300                 }
301
302                 #endregion
303
304                 #region Get Suite Property
305
306                 public static PropertyInfo GetSuiteProperty( Type testClass )
307                 {
308                         if( testClass != null )
309                         {
310                                 PropertyInfo[] properties = testClass.GetProperties( StaticMethods );
311                                 foreach( PropertyInfo property in properties )
312                                 {
313                                         if( Reflect.HasSuiteAttribute( property ) )
314                                         {
315                                                 try 
316                                                 {
317                                                         CheckSuiteProperty(property);
318                                                 }
319                                                 catch( InvalidSuiteException )
320                                                 {
321                                                         return null;
322                                                 }
323                                                 return property;
324                                         }
325                                 }
326                         }
327                         return null;
328                 }
329
330                 private static void CheckSuiteProperty(PropertyInfo property)
331                 {
332                         MethodInfo method = property.GetGetMethod(true);
333                         if(method.ReturnType!=typeof(NUnit.Core.TestSuite))
334                                 throw new InvalidSuiteException("Invalid suite property method signature");
335                         if(method.GetParameters().Length>0)
336                                 throw new InvalidSuiteException("Invalid suite property method signature");
337                 }
338
339                 #endregion
340
341                 #region Categories
342
343                 public static IList GetCategories( MemberInfo member )
344                 {
345                         object[] attributes = member.GetCustomAttributes( CategoryType, false );
346                         IList names = new ArrayList();
347
348                         foreach(CategoryAttribute attribute in attributes) 
349                                 names.Add(attribute.Name);
350                         
351                         return names;
352                 }
353
354                 #endregion
355
356                 #region Invoke Methods
357
358                 public static object Construct( Type type )
359                 {
360                         ConstructorInfo ctor = GetConstructor( type );
361                         if ( ctor == null )
362                                 throw new InvalidTestFixtureException(type.FullName + " does not have a valid constructor");
363                         
364                         return ctor.Invoke( Type.EmptyTypes );
365                 }
366
367                 public static void InvokeMethod( MethodInfo method, object fixture ) 
368                 {
369                         if(method != null)
370                         {
371                                 try
372                                 {
373                                         method.Invoke( fixture, null );
374                                 }
375                                 catch(TargetInvocationException e)
376                                 {
377                                         Exception inner = e.InnerException;
378                                         throw new NunitException("Rethrown",inner);
379                                 }
380                         }
381                 }
382
383                 public static void InvokeSetUp( object fixture )
384                 {
385                         MethodInfo method = GetSetUpMethod( fixture.GetType() );
386                         if(method != null)
387                         {
388                                 InvokeMethod(method, fixture);
389                         }
390                 }
391
392                 public static void InvokeTearDown( object fixture )
393                 {
394                         MethodInfo method = GetTearDownMethod( fixture.GetType() );
395                         if(method != null)
396                         {
397                                 InvokeMethod(method, fixture);
398                         }
399                 }
400
401                 #endregion
402
403                 #region Private Constructor for static-only class
404
405                 private Reflect() { }
406
407                 #endregion
408         }
409 }