Search in sources :

Example 1 with MethodDescriptor

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;
}
Also used : VariableReference(org.evosuite.testcase.variable.VariableReference) MethodDescriptor(org.evosuite.testcase.fm.MethodDescriptor)

Example 2 with MethodDescriptor

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;
}
Also used : EvosuiteError(org.evosuite.testcase.execution.EvosuiteError) CodeUnderTestException(org.evosuite.testcase.execution.CodeUnderTestException) OngoingStubbing(org.mockito.stubbing.OngoingStubbing) VariableReference(org.evosuite.testcase.variable.VariableReference) Method(java.lang.reflect.Method) MethodDescriptor(org.evosuite.testcase.fm.MethodDescriptor) InvocationTargetException(java.lang.reflect.InvocationTargetException) ConstructionFailedException(org.evosuite.ga.ConstructionFailedException) MockitoException(org.mockito.exceptions.base.MockitoException) FalsePositiveException(org.evosuite.runtime.FalsePositiveException) UncompilableCodeException(org.evosuite.testcase.execution.UncompilableCodeException) InvocationTargetException(java.lang.reflect.InvocationTargetException) CodeUnderTestException(org.evosuite.testcase.execution.CodeUnderTestException) InvalidUseOfMatchersException(org.mockito.exceptions.misusing.InvalidUseOfMatchersException) FalsePositiveException(org.evosuite.runtime.FalsePositiveException) GenericAccessibleObject(org.evosuite.utils.generic.GenericAccessibleObject) GenericClass(org.evosuite.utils.generic.GenericClass) InstrumentedClass(org.evosuite.runtime.instrumentation.InstrumentedClass) UncompilableCodeException(org.evosuite.testcase.execution.UncompilableCodeException)

Example 3 with MethodDescriptor

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;
}
Also used : VariableReference(org.evosuite.testcase.variable.VariableReference) MethodDescriptor(org.evosuite.testcase.fm.MethodDescriptor) Type(java.lang.reflect.Type) GenericClass(org.evosuite.utils.generic.GenericClass)

Example 4 with MethodDescriptor

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;
}
Also used : MethodDescriptor(org.evosuite.testcase.fm.MethodDescriptor)

Example 5 with MethodDescriptor

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;
}
Also used : VariableReference(org.evosuite.testcase.variable.VariableReference) MethodDescriptor(org.evosuite.testcase.fm.MethodDescriptor) Map(java.util.Map)

Aggregations

MethodDescriptor (org.evosuite.testcase.fm.MethodDescriptor)6 VariableReference (org.evosuite.testcase.variable.VariableReference)4 GenericClass (org.evosuite.utils.generic.GenericClass)3 Type (java.lang.reflect.Type)2 CaptureType (com.googlecode.gentyref.CaptureType)1 GenericArrayType (java.lang.reflect.GenericArrayType)1 InvocationTargetException (java.lang.reflect.InvocationTargetException)1 Method (java.lang.reflect.Method)1 ParameterizedType (java.lang.reflect.ParameterizedType)1 WildcardType (java.lang.reflect.WildcardType)1 Map (java.util.Map)1 ConstructionFailedException (org.evosuite.ga.ConstructionFailedException)1 FalsePositiveException (org.evosuite.runtime.FalsePositiveException)1 TooManyResourcesException (org.evosuite.runtime.TooManyResourcesException)1 InstrumentedClass (org.evosuite.runtime.instrumentation.InstrumentedClass)1 CodeUnderTestException (org.evosuite.testcase.execution.CodeUnderTestException)1 EvosuiteError (org.evosuite.testcase.execution.EvosuiteError)1 UncompilableCodeException (org.evosuite.testcase.execution.UncompilableCodeException)1 GenericAccessibleObject (org.evosuite.utils.generic.GenericAccessibleObject)1 MockitoException (org.mockito.exceptions.base.MockitoException)1