blob: e108ab997c43578c899b485ab216cac41a23794f [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.dexmaker.mockito;
import com.google.dexmaker.stock.ProxyBuilder;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.plugins.MockMaker;
import org.mockito.plugins.MockSettingsInfo;
import org.mockito.plugins.MockitoInvocationHandler;
/**
* Generates mock instances on Android's runtime.
*/
public final class DexmakerMockMaker implements MockMaker {
private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
public <T> T createMock(Class<T> typeToMock, Class<?>[] extraInterfaces,
MockitoInvocationHandler handler, MockSettingsInfo settings) {
InvocationHandler invocationHandler = new InvocationHandlerAdapter(handler);
if (typeToMock.isInterface()) {
// support interfaces via java.lang.reflect.Proxy
Class[] classesToMock = new Class[extraInterfaces.length + 1];
classesToMock[0] = typeToMock;
System.arraycopy(extraInterfaces, 0, classesToMock, 1, extraInterfaces.length);
@SuppressWarnings("unchecked") // newProxyInstance returns the type of typeToMock
T mock = (T) Proxy.newProxyInstance(typeToMock.getClassLoader(),
classesToMock, invocationHandler);
return mock;
} else {
// support concrete classes via dexmaker's ProxyBuilder
try {
Class<? extends T> proxyClass = ProxyBuilder.forClass(typeToMock)
.implementing(extraInterfaces)
.buildProxyClass();
T mock = unsafeAllocator.newInstance(proxyClass);
Field handlerField = proxyClass.getDeclaredField("$__handler");
handlerField.setAccessible(true);
handlerField.set(mock, invocationHandler);
return mock;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new MockitoException("Failed to mock " + typeToMock, e);
}
}
}
public void resetMock(Object mock, MockitoInvocationHandler newHandler, MockSettingsInfo settings) {
InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
adapter.setHandler(newHandler);
}
public MockitoInvocationHandler getHandler(Object mock) {
InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
return adapter != null ? adapter.getHandler() : null;
}
private InvocationHandlerAdapter getInvocationHandlerAdapter(Object mock) {
if (Proxy.isProxyClass(mock.getClass())) {
InvocationHandler invocationHandler = Proxy.getInvocationHandler(mock);
return invocationHandler instanceof InvocationHandlerAdapter
? (InvocationHandlerAdapter) invocationHandler
: null;
}
if (ProxyBuilder.isProxyClass(mock.getClass())) {
InvocationHandler invocationHandler = ProxyBuilder.getInvocationHandler(mock);
return invocationHandler instanceof InvocationHandlerAdapter
? (InvocationHandlerAdapter) invocationHandler
: null;
}
return null;
}
}