• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.dx.mockito;
18 
19 import com.android.dx.stock.ProxyBuilder;
20 import org.mockito.exceptions.base.MockitoException;
21 import org.mockito.exceptions.stacktrace.StackTraceCleaner;
22 import org.mockito.invocation.MockHandler;
23 import org.mockito.mock.MockCreationSettings;
24 import org.mockito.plugins.MockMaker;
25 import org.mockito.plugins.StackTraceCleanerProvider;
26 
27 import java.lang.reflect.InvocationHandler;
28 import java.lang.reflect.Modifier;
29 import java.lang.reflect.Proxy;
30 import java.util.Set;
31 
32 /**
33  * Generates mock instances on Android's runtime.
34  */
35 public final class DexmakerMockMaker implements MockMaker, StackTraceCleanerProvider {
36     private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
37 
38     @Override
createMock(MockCreationSettings<T> settings, MockHandler handler)39     public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
40         Class<T> typeToMock = settings.getTypeToMock();
41         Set<Class<?>> interfacesSet = settings.getExtraInterfaces();
42         Class<?>[] extraInterfaces = interfacesSet.toArray(new Class[interfacesSet.size()]);
43         InvocationHandler invocationHandler = new InvocationHandlerAdapter(handler);
44 
45         if (typeToMock.isInterface()) {
46             // support interfaces via java.lang.reflect.Proxy
47             Class[] classesToMock = new Class[extraInterfaces.length + 1];
48             classesToMock[0] = typeToMock;
49             System.arraycopy(extraInterfaces, 0, classesToMock, 1, extraInterfaces.length);
50             // newProxyInstance returns the type of typeToMock
51             @SuppressWarnings("unchecked")
52             T mock = (T) Proxy.newProxyInstance(typeToMock.getClassLoader(), classesToMock, invocationHandler);
53             return mock;
54 
55         } else {
56             // support concrete classes via dexmaker's ProxyBuilder
57             try {
58                 Class<? extends T> proxyClass = ProxyBuilder.forClass(typeToMock)
59                         .implementing(extraInterfaces)
60                         .buildProxyClass();
61                 T mock = unsafeAllocator.newInstance(proxyClass);
62                 ProxyBuilder.setInvocationHandler(mock, invocationHandler);
63                 return mock;
64             } catch (RuntimeException e) {
65                 throw e;
66             } catch (Exception e) {
67                 throw new MockitoException("Failed to mock " + typeToMock, e);
68             }
69         }
70     }
71 
72     @Override
resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings)73     public void resetMock(Object mock, MockHandler newHandler, MockCreationSettings settings) {
74         InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
75         adapter.setHandler(newHandler);
76     }
77 
78     @Override
isTypeMockable(final Class<?> type)79     public TypeMockability isTypeMockable(final Class<?> type) {
80         return new TypeMockability() {
81             @Override
82             public boolean mockable() {
83                 return !type.isPrimitive() && !Modifier.isFinal(type.getModifiers());
84             }
85 
86             @Override
87             public String nonMockableReason() {
88                 if (type.isPrimitive()) {
89                     return "primitive type";
90                 }
91 
92                 if (Modifier.isFinal(type.getModifiers())) {
93                     return "final or anonymous class";
94                 }
95 
96                 return "not handled type";
97             }
98         };
99     }
100 
101     @Override
102     public MockHandler getHandler(Object mock) {
103         InvocationHandlerAdapter adapter = getInvocationHandlerAdapter(mock);
104         return adapter != null ? adapter.getHandler() : null;
105     }
106 
107     @Override
108     public StackTraceCleaner getStackTraceCleaner(final StackTraceCleaner defaultCleaner) {
109         return new StackTraceCleaner() {
110             @Override
111             public boolean isIn(StackTraceElement candidate) {
112                 return defaultCleaner.isIn(candidate)
113                         && !candidate.getClassName().endsWith("_Proxy") // dexmaker class proxies
114                         && !candidate.getClassName().startsWith("$Proxy") // dalvik interface proxies
115                         && !candidate.getClassName().startsWith("com.google.dexmaker.mockito.")
116                         && !candidate.getClassName().startsWith("com.android.dx.mockito.");
117             }
118         };
119     }
120 
121     private InvocationHandlerAdapter getInvocationHandlerAdapter(Object mock) {
122         if (mock == null) {
123             return null;
124         }
125         if (Proxy.isProxyClass(mock.getClass())) {
126             InvocationHandler invocationHandler = Proxy.getInvocationHandler(mock);
127             return invocationHandler instanceof InvocationHandlerAdapter
128                     ? (InvocationHandlerAdapter) invocationHandler
129                     : null;
130         }
131 
132         if (ProxyBuilder.isProxyClass(mock.getClass())) {
133             InvocationHandler invocationHandler = ProxyBuilder.getInvocationHandler(mock);
134             return invocationHandler instanceof InvocationHandlerAdapter
135                     ? (InvocationHandlerAdapter) invocationHandler
136                     : null;
137         }
138 
139         return null;
140     }
141 }
142