blob: 97109b794bb623e030276c09df42c64378e5a4ec [file] [log] [blame]
package org.testng.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.testng.ITestContext;
import org.testng.ITestNGMethod;
import org.testng.TestNGException;
import org.testng.internal.annotations.AnnotationHelper;
import org.testng.internal.annotations.IAnnotationFinder;
import org.testng.internal.annotations.IConfiguration;
import org.testng.internal.annotations.IDataProvider;
import org.testng.internal.annotations.IFactory;
import org.testng.internal.annotations.IParameterizable;
import org.testng.internal.annotations.IParameters;
import org.testng.internal.annotations.ITest;
import org.testng.xml.XmlSuite;
/**
* Methods that bind parameters declared in testng.xml to actual values
* used to invoke methods.
*
* @author <a href="mailto:cedric@beust.com">Cedric Beust</a>
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
*
* @@ANNOTATIONS@@
*/
public class Parameters {
private static final String NULL_VALUE= "null";
/**
* Creates the parameters needed for constructing a test class instance.
*/
public static Object[] createInstantiationParameters(Constructor ctor,
String methodAnnotation,
String[] parameterNames,
Map<String, String> params,
XmlSuite xmlSuite)
{
return createParameters(ctor.toString(), ctor.getParameterTypes(),
methodAnnotation, parameterNames, new MethodParameters(params), xmlSuite);
}
/**
* Creates the parameters needed for the specified <tt>@Configuration</tt> <code>Method</code>.
*
* @param m the configuraton method
* @param params
* @param currentTestMethod the current @Test method or <code>null</code> if no @Test is available (this is not
* only in case the configuration method is a @Before/@AfterMethod
* @param finder the annotation finder
* @param xmlSuite
* @return
*/
public static Object[] createConfigurationParameters(Method m,
Map<String, String> params,
ITestNGMethod currentTestMethod,
IAnnotationFinder finder,
XmlSuite xmlSuite,
ITestContext ctx)
{
Method currentTestMeth= currentTestMethod != null ?
currentTestMethod.getMethod() : null;
return createParameters(m, new MethodParameters(params, currentTestMeth, ctx),
finder, xmlSuite, IConfiguration.class, "@Configuration");
}
////////////////////////////////////////////////////////
/**
* @param m
* @param instance
* @return An array of parameters suitable to invoke this method, possibly
* picked from the property file
*/
private static Object[] createParameters(String methodName,
Class[] parameterTypes,
String methodAnnotation,
String[] parameterNames,
MethodParameters params,
XmlSuite xmlSuite)
{
Object[] result = new Object[0];
if(parameterTypes.length > 0) {
List<Object> vResult = new ArrayList<Object>();
checkParameterTypes(methodName, parameterTypes, methodAnnotation, parameterNames);
for(int i =0, j = 0; i < parameterTypes.length; i++) {
if (Method.class.equals(parameterTypes[i])) {
vResult.add(params.currentTestMethod);
}
else if (ITestContext.class.equals(parameterTypes[i])) {
vResult.add(params.context);
}
else {
String p = parameterNames[j];
String value = params.xmlParameters.get(p);
if(null == value) {
// try SysEnv entries
value= System.getProperty(p);
}
if (null == value) {
throw new TestNGException("Parameter '" + p + "' is required by "
+ methodAnnotation
+ " on method "
+ methodName
+ "\nbut has not been defined "
+ (xmlSuite.getFileName() != null ? "in " + xmlSuite.getFileName() : ""));
}
vResult.add(convertType(parameterTypes[i], value, p));
j++;
}
}
result = (Object[]) vResult.toArray(new Object[vResult.size()]);
}
return result;
}
// FIXME
private static void checkParameterTypes(String methodName,
Class[] parameterTypes, String methodAnnotation, String[] parameterNames)
{
if(parameterNames.length == parameterTypes.length) return;
for(int i= parameterTypes.length - 1; i >= parameterNames.length; i--) {
if(!ITestContext.class.equals(parameterTypes[i])
&& !Method.class.equals(parameterTypes[i])) {
throw new TestNGException( "Method " + methodName + " requires "
+ parameterTypes.length + " parameters but "
+ parameterNames.length
+ " were supplied in the "
+ methodAnnotation
+ " annotation.");
}
}
}
//TODO: Cosmin - making this public is not the best solution
public static Object convertType(Class type, String value, String paramName) {
Object result = null;
if(NULL_VALUE.equals(value.toLowerCase())) {
if(type.isPrimitive()) {
Utils.log("Parameters", 2, "Attempt to pass null value to primitive type parameter '" + paramName + "'");
}
return null; // null value must be used
}
if(type == String.class) {
result = value;
}
else if(type == int.class || type == Integer.class) {
result = new Integer(Integer.parseInt(value));
}
else if(type == boolean.class || type == Boolean.class) {
result = Boolean.valueOf(value);
}
else if(type == byte.class || type == Byte.class) {
result = new Byte(Byte.parseByte(value));
}
else if(type == char.class || type == Character.class) {
result = new Character(value.charAt(0));
}
else if(type == double.class || type == Double.class) {
result = new Double(Double.parseDouble(value));
}
else if(type == float.class || type == Float.class) {
result = new Float(Float.parseFloat(value));
}
else if(type == long.class || type == Long.class) {
result = new Long(Long.parseLong(value));
}
else if(type == short.class || type == Short.class) {
result = new Short(Short.parseShort(value));
}
else {
assert false : "Unsupported type parameter : " + type;
}
return result;
}
private static Method findDataProvider(Class clazz, Method m, IAnnotationFinder finder) {
Method result = null;
String dataProviderName = null;
Class dataProviderClass = null;
ITest annotation = AnnotationHelper.findTest(finder, m);
if (annotation != null) {
dataProviderName = annotation.getDataProvider();
dataProviderClass = annotation.getDataProviderClass();
}
if (dataProviderName == null) {
IFactory factory = AnnotationHelper.findFactory(finder, m);
if (factory != null) {
dataProviderName = factory.getDataProvider();
dataProviderClass = null;
}
}
if (null != dataProviderName && ! "".equals(dataProviderName)) {
result = findDataProvider(clazz, finder, dataProviderName, dataProviderClass);
if(null == result) {
throw new TestNGException("Method " + m + " requires a @DataProvider named : "
+ dataProviderName + (dataProviderClass != null ? " in class " + dataProviderClass.getName() : "")
);
}
}
return result;
}
/**
* Find a method that has a @DataProvider(name=name)
*/
private static Method findDataProvider(Class cls, IAnnotationFinder finder,
String name, Class dataProviderClass)
{
boolean shouldBeStatic = false;
if (dataProviderClass != null) {
cls = dataProviderClass;
shouldBeStatic = true;
}
for (Method m : ClassHelper.getAvailableMethods(cls)) {
IDataProvider dp = (IDataProvider) finder.findAnnotation(m, IDataProvider.class);
if (null != dp && (name.equals(dp.getName()) || name.equals(m.getName()))) {
if (shouldBeStatic && (m.getModifiers() & Modifier.STATIC) == 0) {
throw new TestNGException("DataProvider should be static: " + m);
}
return m;
}
}
return null;
}
@SuppressWarnings({"deprecation"})
private static Object[] createParameters(Method m, MethodParameters params,
IAnnotationFinder finder, XmlSuite xmlSuite, Class annotationClass, String atName)
{
List<Object> result = new ArrayList<Object>();
Object[] extraParameters = new Object[0];
//
// Try to find an @Parameters annotation
//
IParameters annotation = (IParameters) finder.findAnnotation(m, IParameters.class);
if(null != annotation) {
String[] parameterNames = annotation.getValue();
extraParameters = createParameters(m.getName(), m.getParameterTypes(),
atName, parameterNames, params, xmlSuite);
}
//
// Else, use the deprecated syntax
//
else {
IParameterizable a = (IParameterizable) finder.findAnnotation(m, annotationClass);
if(null != a && a.getParameters().length > 0) {
String[] parameterNames = a.getParameters();
extraParameters = createParameters(m.getName(), m.getParameterTypes(),
atName, parameterNames, params, xmlSuite);
}
else {
extraParameters = createParameters(m.getName(), m.getParameterTypes(),
atName, new String[0], params, xmlSuite);
}
}
//
// Add the extra parameters we found
//
for (Object p : extraParameters) {
result.add(p);
}
return result.toArray(new Object[result.size()]);
}
/**
* If the method has parameters, fill them in. Either by using a @DataProvider
* if any was provided, or by looking up <parameters> in testng.xml
* @return An Iterator over the values for each parameter of this
* method.
*/
public static Iterator<Object[]> handleParameters(ITestNGMethod testMethod,
Map<String, String> allParameterNames,
Object instance,
MethodParameters methodParams,
XmlSuite xmlSuite,
IAnnotationFinder annotationFinder)
{
Iterator<Object[]> result = null;
//
// Do we have a @DataProvider? If yes, then we have several
// sets of parameters for this method
//
Method dataProvider = findDataProvider(testMethod.getTestClass().getRealClass(),
testMethod.getMethod(),
annotationFinder);
if (null != dataProvider) {
int parameterCount = testMethod.getMethod().getParameterTypes().length;
for (int i = 0; i < parameterCount; i++) {
String n = "param" + i;
allParameterNames.put(n, n);
}
result = MethodHelper.invokeDataProvider(
instance, /* a test instance or null if the dataprovider is static*/
dataProvider,
testMethod,
methodParams.context);
}
else {
//
// Normal case: we have only one set of parameters coming from testng.xml
//
allParameterNames.putAll(methodParams.xmlParameters);
// Create an Object[][] containing just one row of parameters
Object[][] allParameterValuesArray = new Object[1][];
allParameterValuesArray[0] = createParameters(testMethod.getMethod(),
methodParams, annotationFinder, xmlSuite, ITest.class, "@Test");
// Mark that this method needs to have at least a certain
// number of invocations (needed later to call AfterGroups
// at the right time).
testMethod.setParameterInvocationCount(allParameterValuesArray.length);
// Turn it into an Iterable
result = MethodHelper.createArrayIterator(allParameterValuesArray);
}
return result;
}
private static void ppp(String s) {
System.out.println("[Parameters] " + s);
}
/** A parameter passing helper class. */
public static class MethodParameters {
private final Map<String, String> xmlParameters;
private final Method currentTestMethod;
private final ITestContext context;
public MethodParameters(Map<String, String> params) {
this(params, null, null);
}
public MethodParameters(Map<String, String> params, Method m) {
this(params, m, null);
}
public MethodParameters(Map<String, String> params, Method m, ITestContext ctx) {
xmlParameters= params;
currentTestMethod= m;
context= ctx;
}
}
}