1 // ***********************************************************************
2 // Copyright (c) 2008-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
33 using NUnit.Framework.Interfaces;
35 namespace NUnit.Framework.Internal.Builders
38 /// NUnitTestCaseBuilder is a utility class used by attributes
39 /// that build test cases.
41 public class NUnitTestCaseBuilder
43 private readonly Randomizer _randomizer = Randomizer.CreateRandomizer();
44 private readonly TestNameGenerator _nameGenerator;
47 /// Constructs an <see cref="NUnitTestCaseBuilder"/>
49 public NUnitTestCaseBuilder()
51 _nameGenerator = new TestNameGenerator();
55 /// Builds a single NUnitTestMethod, either as a child of the fixture
56 /// or as one of a set of test cases under a ParameterizedTestMethodSuite.
58 /// <param name="method">The MethodInfo from which to construct the TestMethod</param>
59 /// <param name="parentSuite">The suite or fixture to which the new test will be added</param>
60 /// <param name="parms">The ParameterSet to be used, or null</param>
61 /// <returns></returns>
62 public TestMethod BuildTestMethod(IMethodInfo method, Test parentSuite, TestCaseParameters parms)
64 var testMethod = new TestMethod(method, parentSuite)
66 Seed = _randomizer.Next()
69 CheckTestMethodSignature(testMethod, parms);
71 if (parms == null || parms.Arguments == null)
72 testMethod.ApplyAttributesToTest(method.MethodInfo);
74 // NOTE: After the call to CheckTestMethodSignature, the Method
75 // property of testMethod may no longer be the same as the
76 // original MethodInfo, so we don't use it here.
77 string prefix = testMethod.Method.TypeInfo.FullName;
79 // Needed to give proper fullname to test in a parameterized fixture.
80 // Without this, the arguments to the fixture are not included.
81 if (parentSuite != null)
82 prefix = parentSuite.FullName;
86 parms.ApplyToTest(testMethod);
88 if (parms.TestName != null)
90 // The test is simply for efficiency
91 testMethod.Name = parms.TestName.Contains("{")
92 ? new TestNameGenerator(parms.TestName).GetDisplayName(testMethod, parms.OriginalArguments)
97 testMethod.Name = _nameGenerator.GetDisplayName(testMethod, parms.OriginalArguments);
102 testMethod.Name = _nameGenerator.GetDisplayName(testMethod, null);
105 testMethod.FullName = prefix + "." + testMethod.Name;
110 #region Helper Methods
113 /// Helper method that checks the signature of a TestMethod and
114 /// any supplied parameters to determine if the test is valid.
116 /// Currently, NUnitTestMethods are required to be public,
117 /// non-abstract methods, either static or instance,
118 /// returning void. They may take arguments but the _values must
119 /// be provided or the TestMethod is not considered runnable.
121 /// Methods not meeting these criteria will be marked as
122 /// non-runnable and the method will return false in that case.
124 /// <param name="testMethod">The TestMethod to be checked. If it
125 /// is found to be non-runnable, it will be modified.</param>
126 /// <param name="parms">Parameters to be used for this test, or null</param>
127 /// <returns>True if the method signature is valid, false if not</returns>
129 /// The return value is no longer used internally, but is retained
130 /// for testing purposes.
132 private static bool CheckTestMethodSignature(TestMethod testMethod, TestCaseParameters parms)
134 if (testMethod.Method.IsAbstract)
135 return MarkAsNotRunnable(testMethod, "Method is abstract");
137 if (!testMethod.Method.IsPublic)
138 return MarkAsNotRunnable(testMethod, "Method is not public");
140 IParameterInfo[] parameters;
142 if (testMethod.Method.IsGenericMethodDefinition)
144 if (parms != null && parms.Arguments != null)
146 var mi = testMethod.Method.MakeGenericMethodEx(parms.Arguments);
148 return MarkAsNotRunnable(testMethod, "Cannot determine generic types by probing");
149 testMethod.Method = mi;
150 parameters = testMethod.Method.GetParameters();
153 parameters = new IParameterInfo[0];
156 parameters = testMethod.Method.GetParameters();
158 int minArgsNeeded = parameters.Length;
160 parameters = testMethod.Method.GetParameters();
161 int minArgsNeeded = 0;
162 foreach (var parameter in parameters)
164 // IsOptional is supported since .NET 1.1
165 if (!parameter.IsOptional)
169 int maxArgsNeeded = parameters.Length;
171 object[] arglist = null;
172 int argsProvided = 0;
176 testMethod.parms = parms;
177 testMethod.RunState = parms.RunState;
179 arglist = parms.Arguments;
182 argsProvided = arglist.Length;
184 if (testMethod.RunState != RunState.Runnable)
189 ITypeInfo returnType = testMethod.Method.IsGenericMethodDefinition && (parms == null || parms.Arguments == null) ? new TypeWrapper(typeof(void)) : testMethod.Method.ReturnType;
191 ITypeInfo returnType = testMethod.Method.ReturnType;
194 #if NET_4_0 || NET_4_5 || PORTABLE
195 if (AsyncInvocationRegion.IsAsyncOperation(testMethod.Method.MethodInfo))
197 if (returnType.IsType(typeof(void)))
198 return MarkAsNotRunnable(testMethod, "Async test method must have non-void return type");
200 var returnsGenericTask = returnType.IsGenericType &&
201 returnType.GetGenericTypeDefinition() == typeof(System.Threading.Tasks.Task<>);
203 if (returnsGenericTask && (parms == null || !parms.HasExpectedResult))
204 return MarkAsNotRunnable(testMethod,
205 "Async test method must have non-generic Task return type when no result is expected");
207 if (!returnsGenericTask && parms != null && parms.HasExpectedResult)
208 return MarkAsNotRunnable(testMethod,
209 "Async test method must have Task<T> return type when a result is expected");
213 if (returnType.IsType(typeof(void)))
215 if (parms != null && parms.HasExpectedResult)
216 return MarkAsNotRunnable(testMethod, "Method returning void cannot have an expected result");
218 else if (parms == null || !parms.HasExpectedResult)
219 return MarkAsNotRunnable(testMethod, "Method has non-void return value, but no result is expected");
221 if (argsProvided > 0 && maxArgsNeeded == 0)
222 return MarkAsNotRunnable(testMethod, "Arguments provided for method not taking any");
224 if (argsProvided == 0 && minArgsNeeded > 0)
225 return MarkAsNotRunnable(testMethod, "No arguments were provided");
227 if (argsProvided < minArgsNeeded)
228 return MarkAsNotRunnable(testMethod, string.Format("Not enough arguments provided, provide at least {0} arguments.", minArgsNeeded));
230 if (argsProvided > maxArgsNeeded)
231 return MarkAsNotRunnable(testMethod, string.Format("Too many arguments provided, provide at most {0} arguments.", maxArgsNeeded));
233 if (testMethod.Method.IsGenericMethodDefinition && arglist != null)
235 var typeArguments = new GenericMethodHelper(testMethod.Method.MethodInfo).GetTypeArguments(arglist);
236 foreach (Type o in typeArguments)
237 if (o == null || o == TypeHelper.NonmatchingType)
238 return MarkAsNotRunnable(testMethod, "Unable to determine type arguments for method");
241 testMethod.Method = testMethod.Method.MakeGenericMethod(typeArguments);
242 parameters = testMethod.Method.GetParameters();
245 if (arglist != null && parameters != null)
246 TypeHelper.ConvertArgumentList(arglist, parameters);
251 private static bool MarkAsNotRunnable(TestMethod testMethod, string reason)
253 testMethod.RunState = RunState.NotRunnable;
254 testMethod.Properties.Set(PropertyNames.SkipReason, reason);