org.itest.impl.ITestRandomObjectGeneratorImpl Maven / Gradle / Ivy
The newest version!
/**
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Grzegorz Kochański
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package org.itest.impl;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.apache.commons.lang.RandomStringUtils;
import org.itest.ITestConfig;
import org.itest.ITestConstants;
import org.itest.ITestContext;
import org.itest.ITestSuperObject;
import org.itest.annotation.ITestFieldAssignment;
import org.itest.annotation.ITestFieldClass;
import org.itest.exception.ITestException;
import org.itest.exception.ITestIllegalArgumentException;
import org.itest.exception.ITestInitializationException;
import org.itest.exception.ITestMethodExecutionException;
import org.itest.exception.ITestPossibleCycleException;
import org.itest.generator.ITestObjectGenerator;
import org.itest.impl.util.ITestUtils;
import org.itest.param.ITestParamState;
import org.itest.util.reflection.ITestFieldProvider;
import org.itest.util.reflection.ITestFieldProvider.FieldHolder;
import org.itest.util.reflection.ITestTypeTokenProvider;
import com.google.common.reflect.TypeToken;
public class ITestRandomObjectGeneratorImpl implements ITestObjectGenerator {
private static final int MAX_DEPTH = 20;
private static final Class> PROXY_CLASS = Proxy.class;
public static final ITestParamState EMPTY_STATE = new ITestParamStateImpl() {
{
elements = Collections.EMPTY_MAP;
}
};
private static final Random random = new Random();
private static int RANDOM_MAX = 5;
private static int RANDOM_MIN = 2;
private final ITestConfig iTestConfig;
protected ITestTypeTokenProvider typeTokenProvider = new ITestTypeTokenProvider();
protected ITestFieldProvider fieldProvider = new ITestFieldProvider(typeTokenProvider);
public ITestRandomObjectGeneratorImpl(ITestConfig iTestConfig) {
this.iTestConfig = iTestConfig;
}
@Override
public Object generate(Type type, ITestParamState initParam, ITestContext iTestContext) {
try {
return generateRandom(type, iTestContext);
} catch (ITestException e) {
e.addPrefix(type.toString());
throw e;
}
}
@SuppressWarnings("unchecked")
public T generateRandom(Class clazz, ITestContext iTestContext) {
Object res;
ITestParamState iTestState = iTestContext.getCurrentParam();
if ( null != iTestState && null == iTestState.getNames() ) {
res = iTestConfig.getITestValueConverter().convert(clazz, iTestState.getValue());
} else if ( Void.class == clazz || void.class == clazz ) {
res = null;
} else if ( String.class == clazz && (null == iTestState || null == iTestState.getNames()) ) {
res = RandomStringUtils.randomAlphanumeric(20);
} else if ( Long.class == clazz || long.class == clazz ) {
res = new Long(random.nextLong());
} else if ( Integer.class == clazz || int.class == clazz ) {
res = new Integer(random.nextInt());
} else if ( Boolean.class == clazz || boolean.class == clazz ) {
res = random.nextBoolean() ? Boolean.TRUE : Boolean.FALSE;
} else if ( Date.class == clazz ) {
res = new Date(random.nextLong());
} else if ( Double.class == clazz || double.class == clazz ) {
res = random.nextDouble();
} else if ( Float.class == clazz || float.class == clazz ) {
res = random.nextFloat();
} else if ( Character.class == clazz || char.class == clazz ) {
res = RandomStringUtils.random(1).charAt(0);
} else if ( Byte.class == clazz || byte.class == clazz ) {
byte[] randomByte = new byte[1];
random.nextBytes(randomByte);
res = randomByte[0];
} else if ( clazz.isEnum() ) {
res = clazz.getEnumConstants()[random.nextInt(clazz.getEnumConstants().length)];
} else if ( clazz.isArray() ) {
int size = random.nextInt(RANDOM_MAX - RANDOM_MIN) + RANDOM_MIN;
if ( null != iTestState && iTestState.getSizeParam() != null ) {
size = iTestState.getSizeParam();
}
Object array = Array.newInstance(clazz.getComponentType(), size);
for (int i = 0; i < size; i++) {
iTestContext.enter(array, String.valueOf(i));
Object value = generateRandom((Type) clazz.getComponentType(), iTestContext);
Array.set(array, i, value);
iTestContext.leave(value);
}
res = array;
} else {
res = newInstance(clazz, iTestContext);
fillFields(clazz, res, iTestContext);
}
return (T) res;
}
protected Object newInstance(Class clazz, ITestContext iTestContext, Type... typeActualArguments) {
Object res;
Constructor> c = getConstructor(clazz);
Class[] constructorTypes = c.getParameterTypes();
Object[] constructorArgs = new Object[constructorTypes.length];
for (int i = 0; i < constructorTypes.length; i++) {
//TypeToken argType = typeToken.resolveType(constructorTypes[i]);
iTestContext.enter(iTestContext.getCurrentOwner(), "");
iTestContext.setEmptyParam();
constructorArgs[i] = generateRandom((Type) constructorTypes[i], iTestContext);
iTestContext.leave(constructorArgs[i]);
}
try {
res = c.newInstance(constructorArgs);
} catch (Exception e) {
// Object[] args = new Object[constructorArgs.length + 1];
// args[0] = generateRandom(clazz.getEnclosingClass(), null, null, owner, postProcess);
// System.arraycopy(constructorArgs, 0, args, 1, constructorArgs.length);
// try {
// res = c.newInstance(args);
// } catch (Exception ee) {
// throw new RuntimeException(ee);
// }
throw new RuntimeException(e);
}
return res;
}
public T generateRandom(Type type, ITestContext iTestContext) {
ITestParamState iTestState = iTestContext.getCurrentParam();
TypeToken typeToken = typeTokenProvider.getTypeToken(type);
Class> clazz = typeToken.getRawType();
Class> requestedClass = getClassFromParam(iTestState);
if ( null != iTestState && null != iTestState.getAttribute(ITestConstants.ATTRIBUTE_DEFINITION) ) {
ITestParamState iTestStateLoaded = iTestConfig.getITestParamLoader()
.loadITestParam(null == requestedClass ? clazz : requestedClass, iTestState.getAttribute(ITestConstants.ATTRIBUTE_DEFINITION))
.getElement(ITestConstants.THIS);
iTestState = iTestConfig.getITestParamsMerger()
.merge(new ITestParamAssignmentImpl("", iTestStateLoaded), new ITestParamAssignmentImpl("", iTestState));
iTestContext.replaceCurrentState(iTestState);
}
Object res;
if ( ITestParamState.class == clazz ) {
res = processITestState(iTestContext);
} else if ( null != iTestState && null == iTestState.getSizeParam() && null == iTestState.getValue() ) {
res = null;
} else if ( null != iTestState && null != iTestState.getAttribute(ITestConstants.REFERENCE_ATTRIBUTE) ) {
res = iTestContext.findGeneratedObject(iTestState.getAttribute(ITestConstants.REFERENCE_ATTRIBUTE));
} else if ( PROXY_CLASS == requestedClass ) {
res = newDynamicProxy(typeToken, iTestContext);
} else if ( Collection.class.isAssignableFrom(clazz) || (null != requestedClass && Collection.class.isAssignableFrom(requestedClass)) ) {
res = fillCollection(null, typeToken, iTestContext);
} else if ( Map.class.isAssignableFrom(clazz) || (null != requestedClass && Map.class.isAssignableFrom(requestedClass)) ) {
res = fillMap(null, typeToken, iTestContext);
} else if ( null != requestedClass ) {
res = generateRandom(requestedClass, iTestContext);
} else if ( typeToken.getType() instanceof GenericArrayType ) {
int size = random.nextInt(RANDOM_MAX - RANDOM_MIN) + RANDOM_MIN;
if ( null != iTestState && iTestState.getSizeParam() != null ) {
size = iTestState.getSizeParam();
}
TypeToken componentType = typeToken.getComponentType();
Object array = Array.newInstance(componentType.getRawType(), size);
for (int i = 0; i < size; i++) {
iTestContext.enter(array, String.valueOf(i));
Object value = generateRandom((Type) clazz.getComponentType(), iTestContext);
Array.set(array, i, value);
iTestContext.leave(value);
}
res = array;
} else if ( null != iTestState && null == iTestState.getNames() ) {
res = iTestConfig.getITestValueConverter().convert(clazz, iTestState.getValue());
} else if ( clazz.isInterface() ) {
res = newDynamicProxy(typeToken, iTestContext);
} else if ( typeToken.getType() instanceof Class ) {
res = generateRandom(clazz, iTestContext);
} else if ( Class.class == clazz ) {
res = generateRandom(clazz, iTestContext);
} else if ( Enum.class == clazz ) {
Type enumType = typeToken.resolveType(clazz.getTypeParameters()[0]).getType();
res = generateRandom(enumType, iTestContext);
} else {
Type[] typeParameters = clazz.getTypeParameters();
Type[] actualTypeParameters = new Type[typeParameters.length];
for (int i = 0; i < typeParameters.length; i++) {
actualTypeParameters[i] = typeToken.resolveType(typeParameters[i]).getType();
}
res = newInstance(clazz, iTestContext, actualTypeParameters);
fillFields(type, res, iTestContext);
}
return (T) res;
}
private ITestParamState processITestState(ITestContext iTestContext) {
ITestParamState state = iTestContext.getCurrentParam();
ITestParamStateImpl res = null;
if ( null != state ) {
res = new ITestParamStateImpl();
if ( null != state.getAttribute(ITestConstants.REFERENCE_ATTRIBUTE) ) {
ITestParamState ref = iTestContext.findGeneratedState(state.getAttribute(ITestConstants.REFERENCE_ATTRIBUTE));
if ( null == ref.getNames() ) {
res.setValue(ref.getValue());
state = res;
} else {
state = iTestConfig.getITestParamsMerger().merge(new ITestParamAssignmentImpl("", ref), new ITestParamAssignmentImpl("", state));
iTestContext.replaceCurrentState(state);
}
}
Iterable attributes = state.getAttributeNames();
if ( null != attributes ) {
for (String attribute : attributes) {
res.addAttribute(attribute, state.getAttribute(attribute));
}
}
Collection names = state.getNames();
if ( null != names ) {
res.initElements();
for (String name : names) {
iTestContext.enter(res, name);
ITestParamState element = processITestState(iTestContext);
res.addElement(name, element);
iTestContext.leave(element);
}
} else {
res.setValue(state.getValue());
}
}
return res;
}
private Class> getClassFromParam(ITestParamState iTestState) {
Class> clazz = null;
if ( null != iTestState ) {
String reqClass = iTestState.getAttribute(ITestConstants.ATTRIBUTE_CLASS);
if ( null == reqClass && null != iTestState.getElement(ITestConstants.ATTRIBUTE_CLASS) ) {
reqClass = iTestState.getElement(ITestConstants.ATTRIBUTE_CLASS).getValue();
}
if ( null != reqClass ) {
if ( ITestConstants.DYNAMIC.equals(reqClass) ) {
clazz = PROXY_CLASS;
} else {
clazz = iTestConfig.getITestValueConverter().convert(Class.class, reqClass);
}
}
}
return clazz;
}
protected Object newDynamicProxy(TypeToken typeToken, final ITestContext iTestContext) {
final Class> clazz = typeToken.getRawType();
final Map methodResults = new HashMap();
Object res = Proxy.newProxyInstance(clazz.getClassLoader(), new Class>[] { clazz }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String mSignature = ITestUtils.getMethodSingnature(method, true);
if ( methodResults.containsKey(mSignature) ) {
return methodResults.get(mSignature);
}
throw new ITestMethodExecutionException("Implementation of " + clazz.getName() + "." + mSignature + " not provided", null);
}
});
TypeToken t = typeToken;
do {
for (Method m : t.getRawType().getDeclaredMethods()) {
if ( !Modifier.isStatic(m.getModifiers()) ) {
TypeToken mType = typeToken.resolveType(m.getGenericReturnType());
String signature = ITestUtils.getMethodSingnature(m, true);
ITestParamState iTestState = iTestContext.getCurrentParam();
ITestParamState mITestState = iTestState == null ? null : iTestState.getElement(signature);
if ( null == mITestState ) {
signature = ITestUtils.getMethodSingnature(m, false);
}
fillMethod(mType.getType(), m, res, signature, iTestContext, methodResults);
}
}
} while (t.getRawType().getSuperclass() != null && (t = t.getSupertype(t.getRawType().getSuperclass())) != null);
return res;
}
protected void fillMethod(Type mType, Method m, Object res, String mSignature, ITestContext iTestContext, Map methodResults) {
try {
iTestContext.enter(res, mSignature);
ITestParamState mITestState = iTestContext.getCurrentParam();
Object o = generateRandom(mType, iTestContext);
methodResults.put(ITestUtils.getMethodSingnature(m, true), o);
iTestContext.leave(o);
} catch (ITestException e) {
e.addPrefix(mSignature);
throw e;
} catch (IllegalArgumentException e) {
throw new ITestIllegalArgumentException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected void fillFields(Type type, Object o, ITestContext iTestContext) {
if ( iTestContext.depth() > MAX_DEPTH ) {
throw new ITestPossibleCycleException("Possible cycle detected.");
}
ITestParamState itestState = iTestContext.getCurrentParam();
if ( null != itestState && null != itestState.getNames() && o instanceof ITestSuperObject ) {
ITestSuperObject iTestSuperObject = (ITestSuperObject) o;
TypeToken typeToken = typeTokenProvider.getTypeToken(type);
TypeToken contentType = resolveParametrizedType(typeToken, ITestSuperObject.class, 0);
for (String name : itestState.getNames()) {
iTestContext.enter(o, name);
Object value = generate(contentType.getType(), null, iTestContext);
iTestSuperObject.setField(name, value);
iTestContext.leave(value);
}
} else {
Collection fieldHolders = collectFields(type, iTestContext);
for (FieldHolder f : fieldHolders) {
fillField(f.getFieldType(), f.getField(), o, iTestContext);
}
}
}
protected Collection collectFields(Type type, ITestContext iTestContext) {
return fieldProvider.collectFields(type);
}
protected void fillField(Type fType, Field f, Object o, ITestContext iTestContext) {
f.setAccessible(true);
try {
iTestContext.enter(o, f.getName());
ITestParamState fITestState = iTestContext.getCurrentParam();
String fITestValue = fITestState == null ? null : fITestState.getValue();
Object oRes;
// if ( fITestState != null && null != fITestState.getAttribute(ITestConstants.REFERENCE_ATTRIBUTE) ) {
// oRes = iTestContext.findGeneratedObject(fITestState.getAttribute(ITestConstants.REFERENCE_ATTRIBUTE));
// } else
if ( null == fITestState && iTestContext.isStaticAssignmentRegistered(o.getClass(), f.getName()) ) {
iTestContext.registerAssignment(o.getClass(), f.getName());
oRes = null;//TODO: implement it
} else if ( null == fITestState && f.isAnnotationPresent(ITestFieldAssignment.class) ) {
iTestContext.registerAssignment(f.getAnnotation(ITestFieldAssignment.class).value());
oRes = null;//TODO: implement it
} else if ( null == fITestState && f.isAnnotationPresent(ITestFieldClass.class) ) {
iTestContext.setEmptyParam();
oRes = generateRandom(f.getAnnotation(ITestFieldClass.class).value(), iTestContext);
f.set(o, oRes);
// } else if (null != fITestValue) {
// if (fITestValue.startsWith(":")) {
// // TODO: register assignment
// oRes = null;//TODO: implement it
// } else {
// oRes = iTestConfig.getITestValueConverter().convert(f.getType(), fITestValue);
// f.set(o, oRes);
// }
} else {
oRes = generateRandom(fType, iTestContext);
f.set(o, oRes);
}
f.set(o, oRes);
iTestContext.leave(oRes);
} catch (ITestException e) {
e.addPrefix(f.getName());
throw e;
} catch (IllegalArgumentException e) {
throw new ITestIllegalArgumentException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected Object fillMap(Object o, TypeToken typeToken, ITestContext iTestContext) {
ITestParamState iTestState = iTestContext.getCurrentParam();
Map