org.evosuite.testcase.statements.FunctionalMockStatement.java Source code

Java tutorial

Introduction

Here is the source code for org.evosuite.testcase.statements.FunctionalMockStatement.java

Source

/**
 * Copyright (C) 2010-2016 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
package org.evosuite.testcase.statements;

import org.apache.commons.lang3.reflect.TypeUtils;
import org.evosuite.PackageInfo;
import org.evosuite.Properties;
import org.evosuite.assertion.Assertion;
import org.evosuite.ga.ConstructionFailedException;
import org.evosuite.runtime.RuntimeSettings;
import org.evosuite.runtime.classhandling.ClassResetter;
import org.evosuite.runtime.instrumentation.InstrumentedClass;
import org.evosuite.runtime.mock.EvoSuiteMock;
import org.evosuite.runtime.mock.MockList;
import org.evosuite.runtime.util.AtMostOnceLogger;
import org.evosuite.testcase.fm.EvoInvocationListener;
import org.evosuite.testcase.fm.MethodDescriptor;
import org.evosuite.runtime.util.Inputs;
import org.evosuite.testcase.TestCase;
import org.evosuite.testcase.execution.CodeUnderTestException;
import org.evosuite.testcase.execution.EvosuiteError;
import org.evosuite.testcase.execution.Scope;
import org.evosuite.testcase.execution.UncompilableCodeException;
import org.evosuite.testcase.variable.ConstantValue;
import org.evosuite.testcase.variable.VariableReference;
import org.evosuite.utils.generic.GenericAccessibleObject;
import org.evosuite.utils.generic.GenericClass;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.mockito.stubbing.OngoingStubbing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.*;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.withSettings;

/**
 * Statement representing the creation and setup of a functional mock.
 * Recall: a functional mock is different from an environment one (eg for file IO and CPU time).
 * A functional mock instantiation can look like:
 *
 * <p>
 * EvoInvocationListener listener = new EvoInvocationListener(); <br>
 * Foo foo = mock(Foo.class, withSettings().invocationListeners(listener)); <br>
 * when(foo.aMethod(any() ...)).thenReturn( v0, v1, ...); <br>
 * when(foo.anotherMethod(...)).thenReturn( k0, k1, ...); <br>
 * ... <br>
 * listener.activate();
 *
 * <p>
 * All these statements will be represented with a single one, where the return
 * value is the instantiated mock "foo", and the input parameters are all the input
 * parameters of all mocked methods (eg v0, k0), in order.
 *
 * <p>
 * Calls to "listener" are essential during the search (eg when the statement is executed),
 * but will not be part of the final generated JUnit tests (ie not part of toCode())
 *
 * <p>
 * Initially, a functional mock will have 0 input parameters, and no "when" call.
 * After a test is executed, the input parameter lists will be updated based on what
 * "listener" does report. The number of input parameters might vary several times
 * throughout the lifespan of a test during the search (can both increase and decrease).
 *
 * <p>
 * This statement cannot be used to mock the SUT, as it would make no sense whatsoever.
 * However, there might be special cases: eg SUT being an abstract class with no
 * concrete implementation. That would need to be handled specially.
 *
 *
 * <p>
 * Created by Andrea Arcuri on 01/08/15.
 */
public class FunctionalMockStatement extends EntityWithParametersStatement {

    private static final long serialVersionUID = -8177814473724093381L;

    private static final Logger logger = LoggerFactory.getLogger(FunctionalMockStatement.class);

    /**
     * This list needs to be kept sorted
     */
    private final List<MethodDescriptor> mockedMethods;

    /**
     * key -> MethodDescriptor id,
     * Value -> min,max  inclusive of indices on super.parameters
     */
    private final Map<String, int[]> methodParameters;

    private Class<?> targetClass;

    private transient volatile EvoInvocationListener listener;

    private transient Method mockCreator;

    public FunctionalMockStatement(TestCase tc, VariableReference retval, Class<?> targetClass)
            throws IllegalArgumentException {
        super(tc, retval);
        Inputs.checkNull(targetClass);
        this.targetClass = targetClass;
        mockedMethods = new ArrayList<>();
        methodParameters = new LinkedHashMap<>();
        checkTarget();
        assert parameters.isEmpty();
        //setUpMockCreator();
    }

    public FunctionalMockStatement(TestCase tc, Type retvalType, Class<?> targetClass)
            throws IllegalArgumentException {
        super(tc, retvalType);
        Inputs.checkNull(targetClass);

        Class<?> rawType = new GenericClass(retvalType).getRawClass();
        if (!targetClass.equals(rawType)) {
            throw new IllegalArgumentException(
                    "Mismatch between raw type " + rawType + " and target class " + targetClass);
        }

        this.targetClass = targetClass;
        mockedMethods = new ArrayList<>();
        methodParameters = new LinkedHashMap<>();
        checkTarget();
        assert parameters.isEmpty();
        //setUpMockCreator();
    }

    private void setUpMockCreator() {
        ClassLoader loader = targetClass.getClassLoader();
        try {
            Class<?> mockito = loader.loadClass(Mockito.class.getName());
            mockCreator = mockito.getDeclaredMethod("mock", loader.loadClass(Class.class.getName()),
                    loader.loadClass(MockSettings.class.getName()));

        } catch (Exception e) {
            logger.error("Failed to setup mock creator: " + e.getMessage());
        }
    }

    @Override
    public void changeClassLoader(ClassLoader loader) {

        try {
            targetClass = loader.loadClass(targetClass.getName());
            for (MethodDescriptor descriptor : mockedMethods) {
                if (descriptor != null) {
                    descriptor.changeClassLoader(loader);
                }
            }
            if (listener != null) {
                listener.changeClassLoader(loader);
            }
        } catch (ClassNotFoundException e) {
            logger.error("Failed to update target class from new classloader: " + e.getMessage());
        }

        super.changeClassLoader(loader);
    }

    private void checkTarget() {
        if (!canBeFunctionalMocked(targetClass)) {
            throw new IllegalArgumentException("Cannot create a basic functional mock for class " + targetClass);
        }
    }

    public static boolean canBeFunctionalMocked(Type type) {

        Class<?> rawClass = new GenericClass(type).getRawClass();
        final Class<?> targetClass = Properties.getTargetClassAndDontInitialise();

        if (Properties.hasTargetClassBeenLoaded() && (rawClass.equals(targetClass))) {
            return false;
        }

        if (EvoSuiteMock.class.isAssignableFrom(rawClass) || MockList.isAMockClass(rawClass.getName())
                || rawClass.equals(Class.class) || rawClass.isArray() || rawClass.isPrimitive()
                || rawClass.isAnonymousClass() || rawClass.isEnum() ||
                //note: Mockito can handle package-level classes, but we get all kinds of weird exceptions with instrumentation :(
                !Modifier.isPublic(rawClass.getModifiers())) {
            return false;
        }

        if (!InstrumentedClass.class.isAssignableFrom(rawClass) && Modifier.isFinal(rawClass.getModifiers())) {
            /*
            if a class has not been instrumented (eg because belonging to javax.*),
            then if it is final we cannot mock it :(
            recall that instrumentation does remove the final modifiers
             */
            return false;
        }

        //FIXME: tmp fix to avoid mocking any class with package access methods
        try {
            for (Method m : rawClass.getDeclaredMethods()) {

                /*
                Unfortunately, it does not seem there is a "isPackageLevel" method, so we have
                to go by exclusion
                 */

                if (!Modifier.isPublic(m.getModifiers()) && !Modifier.isProtected(m.getModifiers())
                        && !Modifier.isPrivate(m.getModifiers()) && !m.isBridge() && !m.isSynthetic()
                        && !m.getName().equals(ClassResetter.STATIC_RESET)) {
                    return false;
                }
            }
        } catch (NoClassDefFoundError | Exception e) {
            //this could happen if we failed to load the class
            AtMostOnceLogger.warn(logger,
                    "Failed to check if can mock class " + rawClass.getName() + ": " + e.getMessage());
            return false;
        }

        //avoid cases of infinite recursions
        boolean onlySelfReturns = true;
        for (Method m : rawClass.getDeclaredMethods()) {
            if (!rawClass.equals(m.getReturnType())) {
                onlySelfReturns = false;
                break;
            }
        }

        if (onlySelfReturns && rawClass.getDeclaredMethods().length > 0) {
            //avoid weird cases like java.lang.Appendable
            return false;
        }

        //ad-hoc list of classes we should not really mock
        List<Class<?>> avoid = Arrays.asList(
        //add here if needed
        );

        if (avoid.contains(rawClass)) {
            return false;
        }

        return true;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public List<MethodDescriptor> getMockedMethods() {
        return mockedMethods;
    }

    public List<VariableReference> getParameters(String id) throws IllegalArgumentException {
        Inputs.checkNull(id);

        int[] minMax = methodParameters.get(id);
        if (minMax == null) {
            return null;
        }

        List<VariableReference> list = new ArrayList<>();
        for (int i = minMax[0]; i <= minMax[1]; i++) {
            list.add(parameters.get(i));
        }
        return list;
    }

    /**
     * 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) {
            /*
            Tricky case: if no execution yet, then there should be no mocked method yet.
            However, this method is also executed when JUnit source code is generated.
            If this is done in a system test for debugging, then it would be a problem,
            as serialized tests sent from Client to Master have no listener (it has to be
            transient). So, we can just skip it, as info used only for debugging.
             */

            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;
            }

            /*
            need to be a mismatch. However, even in that case, either the current should
            not have reached the limit (and so we could not increase) OR if it is reached
            then the needed number of mocked v has increased.
                
            For example, if limit is 5, and previous is 10, then decreasing by 5 or
             increasing by any amount should have no impact
             */

            if (now.getCounter() != previous.getCounter()
                    && (now.getCounter() < Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT)
                    || previous.getCounter() < Properties.FUNCTIONAL_MOCKING_INPUT_LIMIT) {
                return true;
            }
        }

        return false;
    }

    /**
     * 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();
        mockedMethods.clear(); //important to remove all the no longer used calls

        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());
            int existingParameters; //total number of existing parameters
            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);

                    super.parameters.add(null); //important place holder for following updates
                    added++;
                }
            }

            minMax[0] = mdIndex;
            minMax[1] = (mdIndex + added - 1); //max is inclusive
            assert minMax[1] >= minMax[0] && minMax[0] >= 0; //max >= min
            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;
    }

    public void addMissingInputs(List<VariableReference> inputs) throws IllegalArgumentException {
        Inputs.checkNull(inputs);

        logger.debug("Adding {} missing values", inputs.size());

        if (!inputs.isEmpty()) {

            if (inputs.size() > parameters.size()) {
                //first quick check
                throw new IllegalArgumentException("Not enough parameter place holders");
            }

            int index = 0;
            for (VariableReference ref : inputs) {
                while (parameters.get(index) != null) {
                    index++;
                    if (index >= parameters.size()) {
                        throw new IllegalArgumentException("Not enough parameter place holders");
                    }
                }
                logger.debug("Current input: " + ref + " for expected type " + getExpectedParameterType(index));

                assert ref.isAssignableTo(getExpectedParameterType(index));

                parameters.set(index, ref);
            }
        } //else, nothing to add

        //check if all "holes" have been filled
        for (VariableReference ref : parameters) {
            if (ref == null) {
                throw new IllegalArgumentException("Functional mock not fully set with all needed missing inputs");
            }
        }
    }

    public void fillWithNullRefs() {
        for (int i = 0; i < parameters.size(); i++) {
            VariableReference ref = parameters.get(i);
            if (ref == null) {
                Class<?> expected = getExpectedParameterType(i);
                Object value = null;
                if (expected.isPrimitive()) {
                    //can't fill a primitive with null
                    if (expected.equals(Integer.TYPE)) {
                        value = 0;
                    } else if (expected.equals(Float.TYPE)) {
                        value = 0f;
                    } else if (expected.equals(Double.TYPE)) {
                        value = 0d;
                    } else if (expected.equals(Long.TYPE)) {
                        value = 0L;
                    } else if (expected.equals(Boolean.TYPE)) {
                        value = false;
                    } else if (expected.equals(Short.TYPE)) {
                        value = Short.valueOf("0");
                    } else if (expected.equals(Character.TYPE)) {
                        value = 'a';
                    }
                }
                parameters.set(i, new ConstantValue(tc, new GenericClass(expected), value));
            }
        }
    }

    private Class<?> getExpectedParameterType(int i) {

        for (MethodDescriptor md : mockedMethods) {
            int[] bounds = methodParameters.get(md.getID());
            if (bounds != null && i >= bounds[0] && i <= bounds[1]) {
                return md.getMethod().getReturnType();
            }
        }

        throw new AssertionError("");
    }

    //------------ override methods ---------------

    @Override
    public void addAssertion(Assertion assertion) {
        //never add an assertion to a functional mock
    }

    @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));
        }

        copy.listener = this.listener; //no need to clone, as only read, and created new instance at each new execution

        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;
    }

    @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 = new EvoInvocationListener(retval.getType());

                    //then create the mock
                    Object ret;
                    try {
                        logger.debug("Mockito: create mock for {}", targetClass);

                        ret = mock(targetClass, withSettings().invocationListeners(listener));
                        //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;
                            }

                            Method method = md.getMethod(); //target method, eg foo.aMethod(...)

                            // 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 e) {
                                logger.error("IAE on <" + method + "> when called with "
                                        + Arrays.toString(targetInputs));
                                throw 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++) {

                                    int k = i + index; //the position in flat parameter list
                                    if (k >= parameters.size()) {
                                        throw new RuntimeException(
                                                "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());
                        return; //or should throw an exception?
                    } 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();
                        }
                    }

                    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;
    }

    @Override
    public GenericAccessibleObject<?> getAccessibleObject() {
        return null; //not defined for FM
    }

    @Override
    public boolean isAssignmentStatement() {
        return false;
    }

    @Override
    public boolean same(Statement s) {
        if (this == s)
            return true;
        if (s == null)
            return false;
        if (getClass() != s.getClass())
            return false;

        FunctionalMockStatement fms = (FunctionalMockStatement) s;

        if (fms.parameters.size() != parameters.size())
            return false;

        for (int i = 0; i < parameters.size(); i++) {
            if (!parameters.get(i).same(fms.parameters.get(i)))
                return false;
        }

        if (!retval.same(fms.retval))
            return false;

        if (!targetClass.equals(fms.targetClass)) {
            return false;
        }

        if (fms.mockedMethods.size() != mockedMethods.size()) {
            return false;
        }

        for (int i = 0; i < mockedMethods.size(); i++) {
            if (!mockedMethods.get(i).getID().equals(fms.mockedMethods.get(i).getID())) {
                return false;
            }
        }

        return true;
    }

    @Override
    public String toString() {
        return "mock(" + retval.getType() + ")";
    }

    @Override
    public String getDescriptor() {
        return "()L" + PackageInfo.getNameWithSlash(retval.getVariableClass()) + ";";
    }

    @Override
    public String getDeclaringClassName() {
        return retval.getClassName();
    }

    @Override
    public String getMethodName() {
        return "mock";
    }
}