package org.powermock.api.mockito.internal.expectation;
import org.mockito.Matchers;
import org.mockito.stubbing.OngoingStubbing;
import org.powermock.api.mockito.expectation.ConstructorExpectationSetup;
import org.powermock.api.mockito.expectation.WithExpectedArguments;
import org.powermock.api.mockito.internal.invocation.MockitoNewInvocationControl;
import org.powermock.api.mockito.internal.mockcreation.MockCreator;
import org.powermock.core.MockRepository;
import org.powermock.core.spi.NewInvocationControl;
import org.powermock.core.spi.support.InvocationSubstitute;
import org.powermock.reflect.internal.WhiteboxImpl;
import org.powermock.tests.utils.ArrayMerger;
import org.powermock.tests.utils.impl.ArrayMergerImpl;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public abstract class AbstractConstructorExpectationSetup<T> implements ConstructorExpectationSetup<T> {
protected final Class<T> mockType;
protected final ArrayMerger arrayMerger;
private Class<?>[] parameterTypes = null;
public AbstractConstructorExpectationSetup(Class<T> mockType) {
this.arrayMerger = new ArrayMergerImpl();
this.mockType = mockType;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private <T> OngoingStubbing<T> createNewSubstituteMock(Class<T> type, Class<?>[] parameterTypes,
Object... arguments) throws Exception {
if (type == null) {
throw new IllegalArgumentException("type cannot be null");
}
final Class<T> unmockedType = (Class<T>) WhiteboxImpl.getUnmockedType(type);
if (parameterTypes == null) {
WhiteboxImpl.findUniqueConstructorOrThrowException(type, arguments);
} else {
WhiteboxImpl.getConstructor(unmockedType, parameterTypes);
}
/*
* Check if this type has been mocked before
*/
NewInvocationControl<OngoingStubbing<T>> newInvocationControl = (NewInvocationControl<OngoingStubbing<T>>) MockRepository
.getNewInstanceControl(unmockedType);
if (newInvocationControl == null) {
InvocationSubstitute<T> mock = getMockCreator().createMock(InvocationSubstitute.class, false, false, null, null, (Method[]) null);
newInvocationControl = new MockitoNewInvocationControl(mock);
MockRepository.putNewInstanceControl(type, newInvocationControl);
MockRepository.addObjectsToAutomaticallyReplayAndVerify(WhiteboxImpl.getUnmockedType(type));
}
return newInvocationControl.expectSubstitutionLogic(arguments);
}
abstract MockCreator getMockCreator();
void setParameterTypes(Class<?>[] parameterTypes) {
this.parameterTypes = parameterTypes;
}
OngoingStubbing<T> withArguments(Object[] additionalArguments) throws Exception {
return createNewSubstituteMock(mockType, parameterTypes, additionalArguments);
}
@Override
public OngoingStubbing<T> withArguments(Object firstArgument, Object... additionalArguments) throws Exception {
return createNewSubstituteMock(mockType, parameterTypes, arrayMerger.mergeArrays(Object.class, new Object[]{firstArgument},
additionalArguments));
}
@Override
public OngoingStubbing<T> withAnyArguments() throws Exception {
if (mockType == null) {
throw new IllegalArgumentException("Class to expected cannot be null");
}
final Class<T> unmockedType = (Class<T>) WhiteboxImpl.getUnmockedType(mockType);
final Constructor<?>[] allConstructors = WhiteboxImpl.getAllConstructors(unmockedType);
final Constructor<?> constructor = allConstructors[0];
final Class<?>[] parameterTypes = constructor.getParameterTypes();
Object[] paramArgs = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> paramType = parameterTypes[i];
paramArgs[i] = Matchers.any(paramType);
}
final OngoingStubbing<T> ongoingStubbing = createNewSubstituteMock(mockType, parameterTypes, paramArgs);
Constructor<?>[] otherCtors = new Constructor<?>[allConstructors.length - 1];
System.arraycopy(allConstructors, 1, otherCtors, 0, allConstructors.length - 1);
return new DelegatingToConstructorsOngoingStubbing<T>(otherCtors, ongoingStubbing);
}
@Override
public OngoingStubbing<T> withNoArguments() throws Exception {
return createNewSubstituteMock(mockType, parameterTypes);
}
@Override
public WithExpectedArguments<T> withParameterTypes(Class<?> parameterType, Class<?>... additionalParameterTypes) {
this.parameterTypes = arrayMerger.mergeArrays(Class.class, new Class<?>[]{parameterType}, additionalParameterTypes);
return this;
}
}