DexmakerMockMaker.java revision 60c59ca7eff486f674261b36d2d453f68bf0115d
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.dexmaker.mockito;
18
19import com.google.dexmaker.stock.ProxyBuilder;
20import java.lang.reflect.Field;
21import java.lang.reflect.InvocationHandler;
22import java.lang.reflect.Proxy;
23import org.mockito.exceptions.base.MockitoException;
24import org.mockito.plugins.MockMaker;
25import org.mockito.plugins.MockSettingsInfo;
26import org.mockito.plugins.MockitoInvocationHandler;
27
28/**
29 * Generates mock instances on Android's runtime.
30 */
31public final class DexmakerMockMaker implements MockMaker {
32    private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
33
34    public <T> T createMock(Class<T> typeToMock, Class<?>[] extraInterfaces,
35            MockitoInvocationHandler handler, MockSettingsInfo settings) {
36        InvocationHandler invocationHandler = new InvocationHandlerAdapter(handler);
37
38        if (typeToMock.isInterface()) {
39            // support interfaces via java.lang.reflect.Proxy
40            Class[] classesToMock = new Class[extraInterfaces.length + 1];
41            classesToMock[0] = typeToMock;
42            System.arraycopy(extraInterfaces, 0, classesToMock, 1, extraInterfaces.length);
43            @SuppressWarnings("unchecked") // newProxyInstance returns the type of typeToMock
44            T mock = (T) Proxy.newProxyInstance(typeToMock.getClassLoader(),
45                    classesToMock, invocationHandler);
46            return mock;
47
48        } else {
49            // support concrete classes via dexmaker's ProxyBuilder
50            try {
51                Class<? extends T> proxyClass = ProxyBuilder.forClass(typeToMock)
52                        .implementing(extraInterfaces)
53                        .buildProxyClass();
54                T mock = unsafeAllocator.newInstance(proxyClass);
55                Field handlerField = proxyClass.getDeclaredField("$__handler");
56                handlerField.setAccessible(true);
57                handlerField.set(mock, invocationHandler);
58                return mock;
59            } catch (RuntimeException e) {
60                throw e;
61            } catch (Exception e) {
62                throw new MockitoException("Failed to mock " + typeToMock, e);
63            }
64        }
65    }
66
67    public void resetMock(Object mock, MockitoInvocationHandler newHandler, MockSettingsInfo settings) {
68        InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
69        adapter.setHandler(newHandler);
70    }
71
72    public MockitoInvocationHandler getHandler(Object mock) {
73        InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
74        return adapter != null ? adapter.getHandler() : null;
75    }
76
77    private InvocationHandlerAdapter getInvocationHandlerAdapter(Object mock) {
78        if (Proxy.isProxyClass(mock.getClass())) {
79            InvocationHandler invocationHandler = Proxy.getInvocationHandler(mock);
80            return invocationHandler instanceof InvocationHandlerAdapter
81                    ? (InvocationHandlerAdapter) invocationHandler
82                    : null;
83        }
84
85        if (ProxyBuilder.isProxyClass(mock.getClass())) {
86            InvocationHandler invocationHandler = ProxyBuilder.getInvocationHandler(mock);
87            return invocationHandler instanceof InvocationHandlerAdapter
88                    ? (InvocationHandlerAdapter) invocationHandler
89                    : null;
90        }
91
92        return null;
93    }
94}
95