PageRenderTime 27ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/src/NUnit/core/Builders/NUnitTestCaseBuilder.cs

#
C# | 390 lines | 240 code | 53 blank | 97 comment | 75 complexity | d6967d493eed6d45c78182a57b940c37 MD5 | raw file
Possible License(s): GPL-2.0
  1. // ****************************************************************
  2. // Copyright 2008, Charlie Poole
  3. // This is free software licensed under the NUnit license. You may
  4. // obtain a copy of the license at http://nunit.org.
  5. // ****************************************************************
  6. using System;
  7. using System.Reflection;
  8. using NUnit.Core.Extensibility;
  9. namespace NUnit.Core.Builders
  10. {
  11. /// <summary>
  12. /// Class to build ether a parameterized or a normal NUnitTestMethod.
  13. /// There are four cases that the builder must deal with:
  14. /// 1. The method needs no params and none are provided
  15. /// 2. The method needs params and they are provided
  16. /// 3. The method needs no params but they are provided in error
  17. /// 4. The method needs params but they are not provided
  18. /// This could have been done using two different builders, but it
  19. /// turned out to be simpler to have just one. The BuildFrom method
  20. /// takes a different branch depending on whether any parameters are
  21. /// provided, but all four cases are dealt with in lower-level methods
  22. /// </summary>
  23. public class NUnitTestCaseBuilder : ITestCaseBuilder2
  24. {
  25. private readonly bool allowOldStyleTests = NUnitConfiguration.AllowOldStyleTests;
  26. #region ITestCaseBuilder Methods
  27. /// <summary>
  28. /// Determines if the method can be used to build an NUnit test
  29. /// test method of some kind. The method must normally be marked
  30. /// with an identifying attriute for this to be true. If the test
  31. /// config file sets AllowOldStyleTests to true, then any method beginning
  32. /// "test..." (case-insensitive) is treated as a test unless
  33. /// it is also marked as a setup or teardown method.
  34. ///
  35. /// Note that this method does not check that the signature
  36. /// of the method for validity. If we did that here, any
  37. /// test methods with invalid signatures would be passed
  38. /// over in silence in the test run. Since we want such
  39. /// methods to be reported, the check for validity is made
  40. /// in BuildFrom rather than here.
  41. /// </summary>
  42. /// <param name="method">A MethodInfo for the method being used as a test method</param>
  43. /// <param name="suite">The test suite being built, to which the new test would be added</param>
  44. /// <returns>True if the builder can create a test case from this method</returns>
  45. public bool CanBuildFrom(MethodInfo method)
  46. {
  47. return Reflect.HasAttribute(method, NUnitFramework.TestAttribute, false)
  48. || Reflect.HasAttribute(method, NUnitFramework.TestCaseAttribute, false)
  49. || Reflect.HasAttribute(method, NUnitFramework.TestCaseSourceAttribute, false)
  50. || Reflect.HasAttribute(method, NUnitFramework.TheoryAttribute, false)
  51. || allowOldStyleTests && method.Name.ToLower().StartsWith("test")
  52. && !Reflect.HasAttribute(method, NUnitFramework.SetUpAttribute, true)
  53. && !Reflect.HasAttribute(method, NUnitFramework.TearDownAttribute, true)
  54. && !Reflect.HasAttribute(method, NUnitFramework.FixtureSetUpAttribute, true)
  55. && !Reflect.HasAttribute(method, NUnitFramework.FixtureTearDownAttribute, true);
  56. }
  57. /// <summary>
  58. /// Build a Test from the provided MethodInfo. Depending on
  59. /// whether the method takes arguments and on the availability
  60. /// of test case data, this method may return a single test
  61. /// or a group of tests contained in a ParameterizedMethodSuite.
  62. /// </summary>
  63. /// <param name="method">The MethodInfo for which a test is to be built</param>
  64. /// <param name="suite">The test fixture being populated, or null</param>
  65. /// <returns>A Test representing one or more method invocations</returns>
  66. public Test BuildFrom(MethodInfo method)
  67. {
  68. return BuildFrom(method, null);
  69. }
  70. #region ITestCaseBuilder2 Members
  71. public bool CanBuildFrom(MethodInfo method, Test parentSuite)
  72. {
  73. return CanBuildFrom(method);
  74. }
  75. public Test BuildFrom(MethodInfo method, Test parentSuite)
  76. {
  77. return CoreExtensions.Host.TestCaseProviders.HasTestCasesFor(method)
  78. ? BuildParameterizedMethodSuite(method, parentSuite)
  79. : BuildSingleTestMethod(method, parentSuite, null);
  80. }
  81. #endregion
  82. /// <summary>
  83. /// Builds a ParameterizedMetodSuite containing individual
  84. /// test cases for each set of parameters provided for
  85. /// this method.
  86. /// </summary>
  87. /// <param name="method">The MethodInfo for which a test is to be built</param>
  88. /// <returns>A ParameterizedMethodSuite populated with test cases</returns>
  89. public static Test BuildParameterizedMethodSuite(MethodInfo method, Test parentSuite)
  90. {
  91. ParameterizedMethodSuite methodSuite = new ParameterizedMethodSuite(method);
  92. NUnitFramework.ApplyCommonAttributes(method, methodSuite);
  93. foreach (object source in CoreExtensions.Host.TestCaseProviders.GetTestCasesFor(method, parentSuite))
  94. {
  95. ParameterSet parms;
  96. if (source == null)
  97. {
  98. parms = new ParameterSet();
  99. parms.Arguments = new object[] { null };
  100. }
  101. else
  102. parms = source as ParameterSet;
  103. if (parms == null)
  104. {
  105. if (source.GetType().GetInterface("NUnit.Framework.ITestCaseData") != null)
  106. parms = ParameterSet.FromDataSource(source);
  107. else
  108. {
  109. parms = new ParameterSet();
  110. ParameterInfo[] parameters = method.GetParameters();
  111. Type sourceType = source.GetType();
  112. if (parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(sourceType))
  113. parms.Arguments = new object[] { source };
  114. else if (source is object[])
  115. parms.Arguments = (object[])source;
  116. else if (source is Array)
  117. {
  118. Array array = (Array)source;
  119. if (array.Rank == 1)
  120. {
  121. parms.Arguments = new object[array.Length];
  122. for (int i = 0; i < array.Length; i++)
  123. parms.Arguments[i] = (object)array.GetValue(i);
  124. }
  125. }
  126. else
  127. parms.Arguments = new object[] { source };
  128. }
  129. }
  130. TestMethod test = BuildSingleTestMethod(method, parentSuite, parms);
  131. methodSuite.Add(test);
  132. }
  133. return methodSuite;
  134. }
  135. /// <summary>
  136. /// Builds a single NUnitTestMethod, either as a child of the fixture
  137. /// or as one of a set of test cases under a ParameterizedTestMethodSuite.
  138. /// </summary>
  139. /// <param name="method">The MethodInfo from which to construct the TestMethod</param>
  140. /// <param name="parms">The ParameterSet to be used, or null</param>
  141. /// <returns></returns>
  142. public static NUnitTestMethod BuildSingleTestMethod(MethodInfo method, Test parentSuite, ParameterSet parms)
  143. {
  144. NUnitTestMethod testMethod = new NUnitTestMethod(method);
  145. string prefix = method.ReflectedType.FullName;
  146. if (parentSuite != null)
  147. {
  148. prefix = parentSuite.TestName.FullName;
  149. testMethod.TestName.FullName = prefix + "." + testMethod.TestName.Name;
  150. }
  151. if (CheckTestMethodSignature(testMethod, parms))
  152. {
  153. if (parms == null)
  154. NUnitFramework.ApplyCommonAttributes(method, testMethod);
  155. NUnitFramework.ApplyExpectedExceptionAttribute(method, testMethod);
  156. }
  157. if (parms != null)
  158. {
  159. // NOTE: After the call to CheckTestMethodSignature, the Method
  160. // property of testMethod may no longer be the same as the
  161. // original MethodInfo, so we reassign it here.
  162. method = testMethod.Method;
  163. if (parms.TestName != null)
  164. {
  165. testMethod.TestName.Name = parms.TestName;
  166. testMethod.TestName.FullName = prefix + "." + parms.TestName;
  167. }
  168. else if (parms.OriginalArguments != null)
  169. {
  170. string name = MethodHelper.GetDisplayName(method, parms.OriginalArguments);
  171. testMethod.TestName.Name = name;
  172. testMethod.TestName.FullName = prefix + "." + name;
  173. }
  174. if (parms.Ignored)
  175. {
  176. testMethod.RunState = RunState.Ignored;
  177. testMethod.IgnoreReason = parms.IgnoreReason;
  178. }
  179. if (parms.ExpectedExceptionName != null)
  180. testMethod.exceptionProcessor = new ExpectedExceptionProcessor(testMethod, parms);
  181. foreach (string key in parms.Properties.Keys)
  182. testMethod.Properties[key] = parms.Properties[key];
  183. // Description is stored in parms.Properties
  184. if (parms.Description != null)
  185. testMethod.Description = parms.Description;
  186. }
  187. if (testMethod.BuilderException != null)
  188. testMethod.RunState = RunState.NotRunnable;
  189. return testMethod;
  190. }
  191. #endregion
  192. #region Helper Methods
  193. /// <summary>
  194. /// Helper method that checks the signature of a TestMethod and
  195. /// any supplied parameters to determine if the test is valid.
  196. ///
  197. /// Currently, NUnitTestMethods are required to be public,
  198. /// non-abstract methods, either static or instance,
  199. /// returning void. They may take arguments but the values must
  200. /// be provided or the TestMethod is not considered runnable.
  201. ///
  202. /// Methods not meeting these criteria will be marked as
  203. /// non-runnable and the method will return false in that case.
  204. /// </summary>
  205. /// <param name="testMethod">The TestMethod to be checked. If it
  206. /// is found to be non-runnable, it will be modified.</param>
  207. /// <param name="parms">Parameters to be used for this test, or null</param>
  208. /// <returns>True if the method signature is valid, false if not</returns>
  209. private static bool CheckTestMethodSignature(TestMethod testMethod, ParameterSet parms)
  210. {
  211. if (testMethod.Method.IsAbstract)
  212. {
  213. testMethod.RunState = RunState.NotRunnable;
  214. testMethod.IgnoreReason = "Method is abstract";
  215. return false;
  216. }
  217. if (!testMethod.Method.IsPublic)
  218. {
  219. testMethod.RunState = RunState.NotRunnable;
  220. testMethod.IgnoreReason = "Method is not public";
  221. return false;
  222. }
  223. ParameterInfo[] parameters = testMethod.Method.GetParameters();
  224. int argsNeeded = parameters.Length;
  225. object[] arglist = null;
  226. int argsProvided = 0;
  227. if (parms != null)
  228. {
  229. testMethod.arguments = parms.Arguments;
  230. testMethod.expectedResult = parms.Result;
  231. testMethod.hasExpectedResult = parms.HasExpectedResult;
  232. testMethod.RunState = parms.RunState;
  233. testMethod.IgnoreReason = parms.NotRunReason;
  234. testMethod.BuilderException = parms.ProviderException;
  235. arglist = parms.Arguments;
  236. if (arglist != null)
  237. argsProvided = arglist.Length;
  238. if (testMethod.RunState != RunState.Runnable)
  239. return false;
  240. }
  241. if (!testMethod.Method.ReturnType.Equals(typeof(void)) &&
  242. (parms == null || !parms.HasExpectedResult && parms.ExpectedExceptionName == null))
  243. {
  244. testMethod.RunState = RunState.NotRunnable;
  245. testMethod.IgnoreReason = "Method has non-void return value";
  246. return false;
  247. }
  248. if (argsProvided > 0 && argsNeeded == 0)
  249. {
  250. testMethod.RunState = RunState.NotRunnable;
  251. testMethod.IgnoreReason = "Arguments provided for method not taking any";
  252. return false;
  253. }
  254. if (argsProvided == 0 && argsNeeded > 0)
  255. {
  256. testMethod.RunState = RunState.NotRunnable;
  257. testMethod.IgnoreReason = "No arguments were provided";
  258. return false;
  259. }
  260. //if (argsProvided > argsNeeded)
  261. //{
  262. // ParameterInfo lastParameter = parameters[argsNeeded - 1];
  263. // Type lastParameterType = lastParameter.ParameterType;
  264. // if (lastParameterType.IsArray && lastParameter.IsDefined(typeof(ParamArrayAttribute), false))
  265. // {
  266. // object[] newArglist = new object[argsNeeded];
  267. // for (int i = 0; i < argsNeeded; i++)
  268. // newArglist[i] = arglist[i];
  269. // int length = argsProvided - argsNeeded + 1;
  270. // Array array = Array.CreateInstance(lastParameterType.GetElementType(), length);
  271. // for (int i = 0; i < length; i++)
  272. // array.SetValue(arglist[argsNeeded + i - 1], i);
  273. // newArglist[argsNeeded - 1] = array;
  274. // testMethod.arguments = arglist = newArglist;
  275. // argsProvided = argsNeeded;
  276. // }
  277. //}
  278. if (argsProvided != argsNeeded )
  279. {
  280. testMethod.RunState = RunState.NotRunnable;
  281. testMethod.IgnoreReason = "Wrong number of arguments provided";
  282. return false;
  283. }
  284. #if NET_2_0
  285. if (testMethod.Method.IsGenericMethodDefinition)
  286. {
  287. Type[] typeArguments = GetTypeArgumentsForMethod(testMethod.Method, arglist);
  288. foreach (object o in typeArguments)
  289. if (o == null)
  290. {
  291. testMethod.RunState = RunState.NotRunnable;
  292. testMethod.IgnoreReason = "Unable to determine type arguments for fixture";
  293. return false;
  294. }
  295. testMethod.method = testMethod.Method.MakeGenericMethod(typeArguments);
  296. parameters = testMethod.Method.GetParameters();
  297. for (int i = 0; i < parameters.Length; i++)
  298. {
  299. if (arglist[i].GetType() != parameters[i].ParameterType && arglist[i] is IConvertible)
  300. {
  301. try
  302. {
  303. arglist[i] = Convert.ChangeType(arglist[i], parameters[i].ParameterType);
  304. }
  305. catch (Exception)
  306. {
  307. // Do nothing - the incompatible argument will be reported below
  308. }
  309. }
  310. }
  311. }
  312. #endif
  313. return true;
  314. }
  315. #if NET_2_0
  316. private static Type[] GetTypeArgumentsForMethod(MethodInfo method, object[] arglist)
  317. {
  318. Type[] typeParameters = method.GetGenericArguments();
  319. Type[] typeArguments = new Type[typeParameters.Length];
  320. ParameterInfo[] parameters = method.GetParameters();
  321. for (int typeIndex = 0; typeIndex < typeArguments.Length; typeIndex++)
  322. {
  323. Type typeParameter = typeParameters[typeIndex];
  324. for (int argIndex = 0; argIndex < parameters.Length; argIndex++)
  325. {
  326. if (parameters[argIndex].ParameterType.Equals(typeParameter))
  327. typeArguments[typeIndex] = TypeHelper.BestCommonType(
  328. typeArguments[typeIndex],
  329. arglist[argIndex].GetType());
  330. }
  331. }
  332. return typeArguments;
  333. }
  334. #endif
  335. #endregion
  336. }
  337. }