1 // ***********************************************************************
2 // Copyright (c) 2012-2014 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Reflection;
34 using NUnit.Compatibility;
35 using NUnit.Framework.Interfaces;
36 using NUnit.Framework.Internal;
37 using NUnit.Framework.Internal.Builders;
39 namespace NUnit.Framework.Api
42 /// DefaultTestAssemblyBuilder loads a single assembly and builds a TestSuite
43 /// containing test fixtures present in the assembly.
45 public class DefaultTestAssemblyBuilder : ITestAssemblyBuilder
47 static Logger log = InternalTrace.GetLogger(typeof(DefaultTestAssemblyBuilder));
49 #region Instance Fields
52 /// The default suite builder used by the test assembly builder.
54 ISuiteBuilder _defaultSuiteBuilder;
61 /// Initializes a new instance of the <see cref="DefaultTestAssemblyBuilder"/> class.
63 public DefaultTestAssemblyBuilder()
65 _defaultSuiteBuilder = new DefaultSuiteBuilder();
73 /// Build a suite of tests from a provided assembly
75 /// <param name="assembly">The assembly from which tests are to be built</param>
76 /// <param name="options">A dictionary of options to use in building the suite</param>
78 /// A TestSuite containing the tests found in the assembly
80 public ITest Build(Assembly assembly, IDictionary<string, object> options)
83 log.Debug("Loading {0}", assembly.FullName);
85 log.Debug("Loading {0} in AppDomain {1}", assembly.FullName, AppDomain.CurrentDomain.FriendlyName);
89 string assemblyPath = AssemblyHelper.GetAssemblyName(assembly).Name;
91 string assemblyPath = AssemblyHelper.GetAssemblyName(assembly).FullName;
93 string assemblyPath = AssemblyHelper.GetAssemblyPath(assembly);
96 return Build(assembly, assemblyPath, options);
100 /// Build a suite of tests given the filename of an assembly
102 /// <param name="assemblyName">The filename of the assembly from which tests are to be built</param>
103 /// <param name="options">A dictionary of options to use in building the suite</param>
105 /// A TestSuite containing the tests found in the assembly
107 public ITest Build(string assemblyName, IDictionary<string, object> options)
110 log.Debug("Loading {0}", assemblyName);
112 log.Debug("Loading {0} in AppDomain {1}", assemblyName, AppDomain.CurrentDomain.FriendlyName);
115 TestSuite testAssembly = null;
119 var assembly = AssemblyHelper.Load(assemblyName);
121 if (assembly == null)
122 throw new Exception("assembly is null");
124 testAssembly = Build(assembly, assemblyName, options);
128 testAssembly = new TestAssembly(assemblyName);
129 testAssembly.RunState = RunState.NotRunnable;
130 testAssembly.Properties.Set(PropertyNames.SkipReason, ex.Message);
136 private TestSuite Build(Assembly assembly, string assemblyPath, IDictionary<string, object> options)
138 TestSuite testAssembly = null;
142 if (options.ContainsKey(PackageSettings.DefaultTestNamePattern))
143 TestNameGenerator.DefaultTestNamePattern = options[PackageSettings.DefaultTestNamePattern] as string;
145 if (options.ContainsKey(PackageSettings.TestParameters))
147 string parameters = options[PackageSettings.TestParameters] as string;
148 if (!string.IsNullOrEmpty(parameters))
149 foreach (string param in parameters.Split(new[] { ';' }))
151 int eq = param.IndexOf("=");
153 if (eq > 0 && eq < param.Length - 1)
155 var name = param.Substring(0, eq);
156 var val = param.Substring(eq + 1);
158 TestContext.Parameters.Add(name, val);
163 IList fixtureNames = null;
164 if (options.ContainsKey(PackageSettings.LOAD))
165 fixtureNames = options[PackageSettings.LOAD] as IList;
166 var fixtures = GetFixtures(assembly, fixtureNames);
168 testAssembly = BuildTestAssembly(assembly, assemblyPath, fixtures);
172 testAssembly = new TestAssembly(assemblyPath);
173 testAssembly.RunState = RunState.NotRunnable;
174 testAssembly.Properties.Set(PropertyNames.SkipReason, ex.Message);
182 #region Helper Methods
184 private IList<Test> GetFixtures(Assembly assembly, IList names)
186 var fixtures = new List<Test>();
187 log.Debug("Examining assembly for test fixtures");
189 var testTypes = GetCandidateFixtureTypes(assembly, names);
191 log.Debug("Found {0} classes to examine", testTypes.Count);
193 System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
197 foreach (Type testType in testTypes)
199 var typeInfo = new TypeWrapper(testType);
203 if (_defaultSuiteBuilder.CanBuildFrom(typeInfo))
205 Test fixture = _defaultSuiteBuilder.BuildFrom(typeInfo);
206 fixtures.Add(fixture);
207 testcases += fixture.TestCaseCount;
212 log.Error(ex.ToString());
217 log.Debug("Found {0} fixtures with {1} test cases in {2} seconds", fixtures.Count, testcases, timer.Elapsed);
219 log.Debug("Found {0} fixtures with {1} test cases", fixtures.Count, testcases);
225 private IList<Type> GetCandidateFixtureTypes(Assembly assembly, IList names)
227 var types = assembly.GetTypes();
229 if (names == null || names.Count == 0)
232 var result = new List<Type>();
234 foreach (string name in names)
236 Type fixtureType = assembly.GetType(name);
237 if (fixtureType != null)
238 result.Add(fixtureType);
241 string prefix = name + ".";
243 foreach (Type type in types)
244 if (type.FullName.StartsWith(prefix))
252 private TestSuite BuildTestAssembly(Assembly assembly, string assemblyName, IList<Test> fixtures)
254 TestSuite testAssembly = new TestAssembly(assembly, assemblyName);
256 if (fixtures.Count == 0)
258 testAssembly.RunState = RunState.NotRunnable;
259 testAssembly.Properties.Set(PropertyNames.SkipReason, "Has no TestFixtures");
263 NamespaceTreeBuilder treeBuilder =
264 new NamespaceTreeBuilder(testAssembly);
265 treeBuilder.Add(fixtures);
266 testAssembly = treeBuilder.RootSuite;
269 testAssembly.ApplyAttributesToTest(assembly);
273 testAssembly.Properties.Set(PropertyNames.ProcessID, System.Diagnostics.Process.GetCurrentProcess().Id);
275 testAssembly.Properties.Set(PropertyNames.AppDomain, AppDomain.CurrentDomain.FriendlyName);
278 // TODO: Make this an option? Add Option to sort assemblies as well?
279 // [tronghieu.d] - no need to sort tcs, to sync with tc order of tct-mgr
280 //testAssembly.Sort();