2009-06-12 Bill Holmes <billholmes54@gmail.com>
[mono.git] / mcs / nunit20 / core / TestSuiteBuilder.cs
1 #region Copyright (c) 2002-2003, James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole, Philip A. Craig
2 /************************************************************************************
3 '
4 ' Copyright © 2002-2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
5 ' Copyright © 2000-2003 Philip A. Craig
6 '
7 ' This software is provided 'as-is', without any express or implied warranty. In no 
8 ' event will the authors be held liable for any damages arising from the use of this 
9 ' software.
10
11 ' Permission is granted to anyone to use this software for any purpose, including 
12 ' commercial applications, and to alter it and redistribute it freely, subject to the 
13 ' following restrictions:
14 '
15 ' 1. The origin of this software must not be misrepresented; you must not claim that 
16 ' you wrote the original software. If you use this software in a product, an 
17 ' acknowledgment (see the following) in the product documentation is required.
18 '
19 ' Portions Copyright © 2003 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov, Charlie Poole
20 ' or Copyright © 2000-2003 Philip A. Craig
21 '
22 ' 2. Altered source versions must be plainly marked as such, and must not be 
23 ' misrepresented as being the original software.
24 '
25 ' 3. This notice may not be removed or altered from any source distribution.
26 '
27 '***********************************************************************************/
28 #endregion
29
30 namespace NUnit.Core
31 {
32         using System;
33         using System.IO;
34         using System.Reflection;
35         using System.Collections;
36
37         /// <summary>
38         /// Summary description for TestSuiteBuilder.
39         /// </summary>
40         public class TestSuiteBuilder
41         {
42                 #region Private Fields
43
44                 /// <summary>
45                 /// Hashtable of all test suites we have created to represent namespaces.
46                 /// Used to locate namespace parent suites for fixtures.
47                 /// </summary>
48                 Hashtable namespaceSuites  = new Hashtable();
49
50                 /// <summary>
51                 /// The root of the test suite being created by this builder. This
52                 /// may be a simple TestSuite, an AssemblyTestSuite or a RootTestSuite
53                 /// encompassing multiple assemblies.
54                 /// </summary>
55                 TestSuite rootSuite;
56
57                 /// <summary>
58                 /// The version of the nunit framework referenced by the loaded assembly.
59                 /// </summary>
60                 Version frameworkVersion = null;
61
62                 #endregion
63
64                 #region Properties
65
66                 public Version FrameworkVersion
67                 {
68                         get { return frameworkVersion; }
69                 }
70
71                 #endregion
72
73                 #region Public Methods
74
75                 public Assembly Load(string assemblyName)
76                 {
77                         // Change currentDirectory in case assembly references unmanaged dlls
78                         string currentDirectory = Environment.CurrentDirectory;
79                         string assemblyDirectory = Path.GetDirectoryName( assemblyName );
80                         bool swap = assemblyDirectory != null && assemblyDirectory != string.Empty;
81
82                         try
83                         {
84                                 if ( swap )
85                                         Environment.CurrentDirectory = assemblyDirectory;
86
87 #if !TARGET_JVM
88                                 Assembly assembly = AppDomain.CurrentDomain.Load(Path.GetFileNameWithoutExtension(assemblyName));
89 #else
90                                 Assembly assembly = Assembly.Load(Path.GetFileNameWithoutExtension(assemblyName));
91 #endif
92
93                                 foreach( AssemblyName refAssembly in assembly.GetReferencedAssemblies() )
94                                 {
95                                         if ( refAssembly.Name == "nunit.framework" )
96                                                 this.frameworkVersion = refAssembly.Version;
97                                 }
98
99                                 return assembly;
100                         }
101                         finally
102                         {
103                                 if ( swap )
104                                         Environment.CurrentDirectory = currentDirectory;
105                         }
106                 }
107
108                 public TestSuite Build(string projectName, IList assemblies)
109                 {
110                         RootTestSuite rootSuite = new RootTestSuite( projectName );
111
112                         int assemblyKey = 0;
113                         foreach(string assembly in assemblies)
114                         {
115                                 TestSuite suite = Build( assembly, assemblyKey++ );
116                                 rootSuite.Add( suite );
117                         }
118
119                         return rootSuite;
120                 }
121
122                 public TestSuite Build( string assemblyName )
123                 {
124                         return Build( assemblyName, 0 );
125                 }
126
127                 public TestSuite Build(string assemblyName, string testName )
128                 {
129                         TestSuite suite = null;
130
131                         Assembly assembly = Load(assemblyName);
132
133                         if(assembly != null)
134                         {
135                                 Type testType = assembly.GetType(testName);
136                                 if( testType != null )
137                                         return MakeSuite( testType );
138
139                                 // Assume that testName is a namespace
140                                 string prefix = testName + '.';
141
142 #if !TARGET_JVM
143                                 Type[] testTypes = assembly.GetExportedTypes();
144 #else
145                                 Type[] testTypes = GetAssemblyExportedTypes(assembly);
146 #endif
147                                 int testFixtureCount = 0;
148
149                                 foreach(Type type in testTypes)
150                                 {
151                                         if( CanMakeSuite( type ) && type.Namespace != null )
152                                         {
153                                                 if( type.Namespace == testName || type.Namespace.StartsWith(prefix) )
154                                                 {
155                                                         suite = BuildFromNameSpace(testName, 0);
156                                                 
157                                                         //suite.Add( new TestFixture( type ) );
158                                                         suite.Add( MakeSuite( type ) );
159                                                         testFixtureCount++;
160                                                 }
161                                         }
162                                 }
163
164                                 return testFixtureCount == 0 ? null : rootSuite;
165                         }
166
167                         return suite;
168                 }
169
170                 public TestSuite Build( IList assemblies, string testName )
171                 {
172                         TestSuite suite = null;
173
174                         foreach(string assemblyName in assemblies)
175                         {
176                                 suite = Build( assemblyName, testName );
177                                 if ( suite != null ) break;
178                         }
179
180                         return suite;
181                 }
182                 
183                 // TODO: Only used in tests
184                 public object BuildTestFixture( Type fixtureType )
185                 {
186                         Reflect.CheckFixtureType( fixtureType );
187
188                         object testFixture;
189                         ConstructorInfo ctor = Reflect.GetConstructor( fixtureType );
190
191                         try
192                         {
193                                 testFixture = ctor.Invoke( Type.EmptyTypes );
194                         }
195                         catch( Exception ex )
196                         {
197                                 throw new InvalidTestFixtureException( ctor.Name + " threw a exception", ex );
198                         }
199
200                         if(testFixture == null) throw new InvalidTestFixtureException(ctor.Name + " cannot be invoked");
201
202                         return testFixture;
203                 }
204
205                 #endregion
206
207                 #region Nested TypeFilter Class
208
209                 private class TypeFilter
210                 {
211                         private string rootNamespace;
212
213                         TypeFilter( string rootNamespace ) 
214                         {
215                                 this.rootNamespace = rootNamespace;
216                         }
217
218                         public bool Include( Type type )
219                         {
220                                 if ( type.Namespace == rootNamespace )
221                                         return true;
222
223                                 return type.Namespace.StartsWith( rootNamespace + '.' );
224                         }
225                 }
226
227                 #endregion
228
229                 #region Helper Methods
230
231                 private TestSuite BuildFromNameSpace( string nameSpace, int assemblyKey )
232                 {
233                         if( nameSpace == null || nameSpace  == "" ) return rootSuite;
234                         TestSuite suite = (TestSuite)namespaceSuites[nameSpace];
235                         if(suite!=null) return suite;
236
237                         int index = nameSpace.LastIndexOf(".");
238                         string prefix = string.Format( "[{0}]", assemblyKey );
239                         if( index == -1 )
240                         {
241                                 suite = new NamespaceSuite( nameSpace, assemblyKey );
242                                 if ( rootSuite == null )
243                                         rootSuite = suite;
244                                 else
245                                         rootSuite.Add(suite);
246                                 namespaceSuites[nameSpace]=suite;
247                         }
248                         else
249                         {
250                                 string parentNameSpace = nameSpace.Substring( 0,index );
251                                 TestSuite parent = BuildFromNameSpace( parentNameSpace, assemblyKey );
252                                 string suiteName = nameSpace.Substring( index+1 );
253                                 suite = new NamespaceSuite( parentNameSpace, suiteName, assemblyKey );
254                                 parent.Add( suite );
255                                 namespaceSuites[nameSpace] = suite;
256                         }
257
258                         return suite;
259                 }
260
261                 private TestSuite Build( string assemblyName, int assemblyKey )
262                 {
263                         TestSuiteBuilder builder = new TestSuiteBuilder();
264
265                         Assembly assembly = Load( assemblyName );
266
267                         builder.rootSuite = new AssemblyTestSuite( assemblyName, assemblyKey );
268                         int testFixtureCount = 0;
269 #if !TARGET_JVM
270                         Type[] testTypes = assembly.GetExportedTypes();
271 #else
272                         Type[] testTypes = GetAssemblyExportedTypes(assembly);
273 #endif
274                         foreach(Type testType in testTypes)
275                         {
276                                 if( CanMakeSuite( testType ) )
277                                 {
278                                         testFixtureCount++;
279                                         string namespaces = testType.Namespace;
280                                         TestSuite suite = builder.BuildFromNameSpace( namespaces, assemblyKey );
281
282                                         //suite.Add( new TestFixture( testType ) );
283                                         suite.Add( MakeSuite( testType ) );
284                                 }
285                         }
286
287                         if(testFixtureCount == 0)
288                         {
289                                 builder.rootSuite.ShouldRun = false;
290                                 builder.rootSuite.IgnoreReason = "Has no TestFixtures";
291                         }
292
293                         return builder.rootSuite;
294                 }
295
296 #if TARGET_JVM
297                 private Type[] GetAssemblyExportedTypes(Assembly assembly)
298                 {
299                         Type[] allTypes = null;
300                         try
301                         {
302                                 allTypes = assembly.GetTypes();
303                         }
304                         catch (Exception e) 
305                         {
306                             System.Console.WriteLine("ReflectionTypeLoadException error");
307                         }
308                         ArrayList tmpTestTypes = new ArrayList(allTypes.Length);
309                         foreach(Type current in allTypes)
310                         {
311                                 if (current.IsPublic || current.IsNestedPublic)
312                                 {
313                                         tmpTestTypes.Add(current);
314                                 }
315                         }
316                         return (Type[])tmpTestTypes.ToArray(typeof(Type));
317                 }
318 #endif
319                 /// <summary>
320                 /// Helper routine that makes a suite from either a TestFixture or
321                 /// a legacy Suite property.
322                 /// </summary>
323                 /// <param name="testType"></param>
324                 /// <returns></returns>
325                 private TestSuite MakeSuite( Type testType )
326                 {
327                         TestSuite suite = null;
328
329                         if(testType != null)
330                         {
331                                 if( TestFixture.IsValidType( testType ) )
332                                 {
333                                         suite = new TestFixture( testType );
334                                 }
335                                 else if( LegacySuite.IsValidType( testType ) )
336                                 {
337                                         suite = new LegacySuite( testType );
338                                 }
339                         }
340                         
341                         return suite;
342                 }
343
344                 private bool CanMakeSuite( Type testType )
345                 {
346                         //return TestFixture.IsValidType( testType ) || LegacySuite.IsValidType( testType );
347                         return TestFixture.IsValidType( testType );
348                 }
349         }
350
351         #endregion
352 }