use of org.evosuite.testcase.fm.MethodDescriptor in project evosuite by EvoSuite.
the class FunctionalMockStatement method copy.
@Override
public Statement copy(TestCase newTestCase, int offset) {
FunctionalMockStatement copy = new FunctionalMockStatement(newTestCase, retval.getType(), targetClass);
for (VariableReference r : this.parameters) {
copy.parameters.add(r.copy(newTestCase, offset));
}
// no need to clone, as only read, and created new instance at each new execution
copy.listener = this.listener;
for (MethodDescriptor md : this.mockedMethods) {
copy.mockedMethods.add(md.getCopy());
}
for (Map.Entry<String, int[]> entry : methodParameters.entrySet()) {
int[] array = entry.getValue();
int[] copiedArray = array == null ? null : new int[] { array[0], array[1] };
copy.methodParameters.put(entry.getKey(), copiedArray);
}
return copy;
}
use of org.evosuite.testcase.fm.MethodDescriptor in project evosuite by EvoSuite.
the class FunctionalMockStatement method execute.
@Override
public Throwable execute(Scope scope, PrintStream out) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException {
Throwable exceptionThrown = null;
try {
return super.exceptionHandler(new Executer() {
@Override
public void execute() throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, InstantiationException, CodeUnderTestException {
// First create the listener
listener = createInvocationListener();
// then create the mock
Object ret;
try {
logger.debug("Mockito: create mock for {}", targetClass);
ret = mock(targetClass, createMockSettings());
// ret = mockCreator.invoke(null,targetClass,withSettings().invocationListeners(listener));
// execute all "when" statements
int index = 0;
logger.debug("Mockito: going to mock {} different methods", mockedMethods.size());
for (MethodDescriptor md : mockedMethods) {
if (!md.shouldBeMocked()) {
// no need to mock a method that returns void
logger.debug("Mockito: method {} cannot be mocked", md.getMethodName());
continue;
}
// target method, eg foo.aMethod(...)
Method method = md.getMethod();
// this is needed if method is protected: it couldn't be called here, although fine in
// the generated JUnit tests
method.setAccessible(true);
// target inputs
Object[] targetInputs = new Object[md.getNumberOfInputParameters()];
for (int i = 0; i < targetInputs.length; i++) {
logger.debug("Mockito: executing matcher {}/{}", (1 + i), targetInputs.length);
targetInputs[i] = md.executeMatcher(i);
}
logger.debug("Mockito: going to invoke method {} with {} matchers", method.getName(), targetInputs.length);
if (!method.getDeclaringClass().isAssignableFrom(ret.getClass())) {
String msg = "Mismatch between callee's class " + ret.getClass() + " and method's class " + method.getDeclaringClass();
msg += "\nTarget class classloader " + targetClass.getClassLoader() + " vs method's classloader " + method.getDeclaringClass().getClassLoader();
throw new EvosuiteError(msg);
}
// actual call foo.aMethod(...)
Object targetMethodResult;
try {
if (targetInputs.length == 0) {
targetMethodResult = method.invoke(ret);
} else {
targetMethodResult = method.invoke(ret, targetInputs);
}
} catch (InvocationTargetException e) {
logger.error("Invocation of mocked {}.{}() threw an exception. " + "This means the method was not mocked", targetClass.getName(), method.getName());
throw e;
} catch (IllegalArgumentException | IllegalAccessError e) {
// FIXME: Happens for reasons I don't understand. By throwing a CodeUnderTestException EvoSuite
// will just ignore that mocking statement and continue, instead of crashing
logger.error("IAE on <" + method + "> when called with " + Arrays.toString(targetInputs));
throw new CodeUnderTestException(e);
}
// when(...)
logger.debug("Mockito: call 'when'");
OngoingStubbing<Object> retForThen = Mockito.when(targetMethodResult);
// thenReturn(...)
Object[] thenReturnInputs = null;
try {
int size = Math.min(md.getCounter(), Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT);
thenReturnInputs = new Object[size];
for (int i = 0; i < thenReturnInputs.length; i++) {
// the position in flat parameter list
int k = i + index;
if (k >= parameters.size()) {
// throw new RuntimeException("EvoSuite ERROR: index " + k + " out of " + parameters.size());
throw new CodeUnderTestException(new FalsePositiveException("EvoSuite ERROR: index " + k + " out of " + parameters.size()));
}
VariableReference parameterVar = parameters.get(i + index);
thenReturnInputs[i] = parameterVar.getObject(scope);
CodeUnderTestException codeUnderTestException = null;
if (thenReturnInputs[i] == null && method.getReturnType().isPrimitive()) {
codeUnderTestException = new CodeUnderTestException(new NullPointerException());
} else if (thenReturnInputs[i] != null && !TypeUtils.isAssignable(thenReturnInputs[i].getClass(), method.getReturnType())) {
codeUnderTestException = new CodeUnderTestException(new UncompilableCodeException("Cannot assign " + parameterVar.getVariableClass().getName() + " to " + method.getReturnType()));
}
if (codeUnderTestException != null) {
throw codeUnderTestException;
}
thenReturnInputs[i] = fixBoxing(thenReturnInputs[i], method.getReturnType());
}
} catch (Exception e) {
// be sure "then" is always called after a "when", otherwise Mockito might end up in
// a inconsistent state
retForThen.thenThrow(new RuntimeException("Failed to setup mock: " + e.getMessage()));
throw e;
}
// final call when(...).thenReturn(...)
logger.debug("Mockito: executing 'thenReturn'");
if (thenReturnInputs == null || thenReturnInputs.length == 0) {
retForThen.thenThrow(new RuntimeException("No valid return value"));
} else if (thenReturnInputs.length == 1) {
retForThen.thenReturn(thenReturnInputs[0]);
} else {
Object[] values = Arrays.copyOfRange(thenReturnInputs, 1, thenReturnInputs.length);
retForThen.thenReturn(thenReturnInputs[0], values);
}
index += thenReturnInputs == null ? 0 : thenReturnInputs.length;
}
} catch (CodeUnderTestException e) {
throw e;
} catch (java.lang.NoClassDefFoundError e) {
AtMostOnceLogger.error(logger, "Cannot use Mockito on " + targetClass + " due to failed class initialization: " + e.getMessage());
// or should throw an exception?
return;
} catch (MockitoException | IllegalAccessException | IllegalAccessError | IllegalArgumentException e) {
// FIXME: Happens for reasons I don't understand. By throwing a CodeUnderTestException EvoSuite
// will just ignore that mocking statement and continue, instead of crashing
AtMostOnceLogger.error(logger, "Cannot use Mockito on " + targetClass + " due to IAE: " + e.getMessage());
// or should throw an exception?
throw new CodeUnderTestException(e);
} catch (Throwable t) {
AtMostOnceLogger.error(logger, "Failed to use Mockito on " + targetClass + ": " + t.getMessage());
throw new EvosuiteError(t);
}
// finally, activate the listener
listener.activate();
try {
retval.setObject(scope, ret);
} catch (CodeUnderTestException e) {
throw e;
} catch (Throwable e) {
throw new EvosuiteError(e);
}
}
/**
* a "char" can be used for a "int". But problem is that Mockito takes as input
* Object, and so those get boxed. However, a Character cannot be used for a "int",
* so we need to be sure to convert it here
*
* @param value
* @param expectedType
* @return
*/
private Object fixBoxing(Object value, Class<?> expectedType) {
if (!expectedType.isPrimitive()) {
return value;
}
Class<?> valuesClass = value.getClass();
assert !valuesClass.isPrimitive();
if (expectedType.equals(Integer.TYPE)) {
if (valuesClass.equals(Character.class)) {
value = (int) ((Character) value).charValue();
} else if (valuesClass.equals(Byte.class)) {
value = (int) ((Byte) value).intValue();
} else if (valuesClass.equals(Short.class)) {
value = (int) ((Short) value).intValue();
}
}
if (expectedType.equals(Double.TYPE)) {
if (valuesClass.equals(Integer.class)) {
value = (double) ((Integer) value).intValue();
} else if (valuesClass.equals(Byte.class)) {
value = (double) ((Byte) value).intValue();
} else if (valuesClass.equals(Character.class)) {
value = (double) ((Character) value).charValue();
} else if (valuesClass.equals(Short.class)) {
value = (double) ((Short) value).intValue();
} else if (valuesClass.equals(Long.class)) {
value = (double) ((Long) value).longValue();
} else if (valuesClass.equals(Float.class)) {
value = (double) ((Float) value).floatValue();
}
}
if (expectedType.equals(Float.TYPE)) {
if (valuesClass.equals(Integer.class)) {
value = (float) ((Integer) value).intValue();
} else if (valuesClass.equals(Byte.class)) {
value = (float) ((Byte) value).intValue();
} else if (valuesClass.equals(Character.class)) {
value = (float) ((Character) value).charValue();
} else if (valuesClass.equals(Short.class)) {
value = (float) ((Short) value).intValue();
} else if (valuesClass.equals(Long.class)) {
value = (float) ((Long) value).longValue();
}
}
if (expectedType.equals(Long.TYPE)) {
if (valuesClass.equals(Integer.class)) {
value = (long) ((Integer) value).intValue();
} else if (valuesClass.equals(Byte.class)) {
value = (long) ((Byte) value).intValue();
} else if (valuesClass.equals(Character.class)) {
value = (long) ((Character) value).charValue();
} else if (valuesClass.equals(Short.class)) {
value = (long) ((Short) value).intValue();
}
}
if (expectedType.equals(Short.TYPE)) {
if (valuesClass.equals(Integer.class)) {
value = (short) ((Integer) value).intValue();
} else if (valuesClass.equals(Byte.class)) {
value = (short) ((Byte) value).intValue();
} else if (valuesClass.equals(Short.class)) {
value = (short) ((Short) value).intValue();
} else if (valuesClass.equals(Character.class)) {
value = (short) ((Character) value).charValue();
} else if (valuesClass.equals(Long.class)) {
value = (short) ((Long) value).intValue();
}
}
if (expectedType.equals(Byte.TYPE)) {
if (valuesClass.equals(Integer.class)) {
value = (byte) ((Integer) value).intValue();
} else if (valuesClass.equals(Short.class)) {
value = (byte) ((Short) value).intValue();
} else if (valuesClass.equals(Byte.class)) {
value = (byte) ((Byte) value).intValue();
} else if (valuesClass.equals(Character.class)) {
value = (byte) ((Character) value).charValue();
} else if (valuesClass.equals(Long.class)) {
value = (byte) ((Long) value).intValue();
}
}
return value;
}
@Override
public Set<Class<? extends Throwable>> throwableExceptions() {
Set<Class<? extends Throwable>> t = new LinkedHashSet<>();
t.add(InvocationTargetException.class);
return t;
}
});
} catch (InvocationTargetException e) {
exceptionThrown = e.getCause();
}
return exceptionThrown;
}
use of org.evosuite.testcase.fm.MethodDescriptor in project evosuite by EvoSuite.
the class FunctionalMockStatement method updateMockedMethods.
/**
* Based on most recent test execution, update the mocking configuration.
* After calling this method, it is <b>necessary</b> to provide the missing
* VariableReferences, if any, using addMissingInputs.
*
* @return a ordered, non-null list of types of missing new inputs that will need to be provided
*/
public List<Type> updateMockedMethods() throws ConstructionFailedException {
logger.debug("Executing updateMockedMethods. Parameter size: " + parameters.size());
List<Type> list = new ArrayList<>();
assert !super.parameters.contains(null);
assert mockedMethods.size() == methodParameters.size();
List<VariableReference> copy = new ArrayList<>(super.parameters);
assert copy.size() == super.parameters.size();
super.parameters.clear();
// important to remove all the no longer used calls
mockedMethods.clear();
Map<String, int[]> mpCopy = new LinkedHashMap<>();
List<MethodDescriptor> executed = listener.getCopyOfMethodDescriptors();
int mdIndex = 0;
for (MethodDescriptor md : executed) {
mockedMethods.add(md);
if (!md.shouldBeMocked() || md.getCounter() == 0) {
// void method or not called, so no parameter needed for it
mpCopy.put(md.getID(), null);
continue;
}
int added = 0;
logger.debug("Method called on mock object: " + md.getMethod());
// infer parameter mapping of current vars from previous execution, if any
int[] minMax = methodParameters.get(md.getID());
// total number of existing parameters
int existingParameters;
if (minMax == null) {
// before it was not called
minMax = new int[] { -1, -1 };
existingParameters = 0;
} else {
assert minMax[1] >= minMax[0] && minMax[0] >= 0;
assert minMax[1] < copy.size() : "Max=" + minMax[1] + " but n=" + copy.size();
existingParameters = 1 + (minMax[1] - minMax[0]);
}
assert existingParameters <= Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT;
// check if less calls
if (existingParameters > md.getCounter()) {
// now the method has been called less times,
// so remove the last calls, ie decrease counter
minMax[1] -= (existingParameters - md.getCounter());
}
if (existingParameters > 0) {
for (int i = minMax[0]; i <= minMax[1]; i++) {
// align super class data structure
super.parameters.add(copy.get(i));
added++;
}
}
// check if rather more calls
if (existingParameters < md.getCounter()) {
for (int i = existingParameters; i < md.getCounter() && i < Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT; i++) {
// Create a copy as the typemap is stored in the class during generic instantiation
// but we might want to have a different type for each call of the same method invocation
GenericClass calleeClass = new GenericClass(retval.getGenericClass());
Type returnType = md.getGenericMethodFor(calleeClass).getGeneratedType();
assert !returnType.equals(Void.TYPE);
logger.debug("Return type: " + returnType + " for retval " + retval.getGenericClass());
list.add(returnType);
// important place holder for following updates
super.parameters.add(null);
added++;
}
}
minMax[0] = mdIndex;
// max is inclusive
minMax[1] = (mdIndex + added - 1);
// max >= min
assert minMax[1] >= minMax[0] && minMax[0] >= 0;
assert super.parameters.size() == minMax[1] + 1;
mpCopy.put(md.getID(), minMax);
mdIndex += added;
}
methodParameters.clear();
methodParameters.putAll(mpCopy);
for (MethodDescriptor md : mockedMethods) {
if (!methodParameters.containsKey(md.getID())) {
methodParameters.put(md.getID(), null);
}
}
return list;
}
use of org.evosuite.testcase.fm.MethodDescriptor in project evosuite by EvoSuite.
the class FunctionalMockStatement method doesNeedToUpdateInputs.
/**
* Check if the last execution of the test case has led a change in the usage of the mock.
* This will result in adding/removing variable references
*
* @return
*/
public boolean doesNeedToUpdateInputs() {
if (listener == null) {
assert mockedMethods.isEmpty() || RuntimeSettings.isRunningASystemTest;
return false;
}
List<MethodDescriptor> executed = listener.getCopyOfMethodDescriptors();
if (executed.size() != mockedMethods.size()) {
return true;
}
for (int i = 0; i < executed.size(); i++) {
MethodDescriptor previous = mockedMethods.get(i);
MethodDescriptor now = executed.get(i);
if (!previous.getID().equals(now.getID())) {
return true;
}
if (!now.shouldBeMocked()) {
/*
Do not change in the usage of non-mockable methods, because anyway
we do not have any VarRef for them
*/
continue;
}
if (now.getCounter() != previous.getCounter() && (now.getCounter() < Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT) || previous.getCounter() < Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT) {
return true;
}
}
return false;
}
use of org.evosuite.testcase.fm.MethodDescriptor in project evosuite by EvoSuite.
the class FunctionalMockForAbstractClassStatement method copy.
@Override
public Statement copy(TestCase newTestCase, int offset) {
FunctionalMockForAbstractClassStatement copy = new FunctionalMockForAbstractClassStatement(newTestCase, retval.getType(), targetClass);
for (VariableReference r : this.parameters) {
copy.parameters.add(r.copy(newTestCase, offset));
}
// no need to clone, as only read, and created new instance at each new execution
copy.listener = this.listener;
for (MethodDescriptor md : this.mockedMethods) {
copy.mockedMethods.add(md.getCopy());
}
for (Map.Entry<String, int[]> entry : methodParameters.entrySet()) {
int[] array = entry.getValue();
int[] copiedArray = array == null ? null : new int[] { array[0], array[1] };
copy.methodParameters.put(entry.getKey(), copiedArray);
}
return copy;
}
Aggregations