• 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.inline;
18 
19 import com.android.dx.stock.ProxyBuilder;
20 
21 import org.mockito.Mockito;
22 import org.mockito.invocation.InvocationFactory.RealMethodBehavior;
23 import org.mockito.invocation.MockHandler;
24 import org.mockito.mock.MockCreationSettings;
25 
26 import java.lang.reflect.InvocationHandler;
27 import java.lang.reflect.Method;
28 
29 import static org.mockito.Mockito.withSettings;
30 
31 /**
32  * Handles proxy and entry hook method invocations added by
33  * {@link InlineDexmakerMockMaker#createMock(MockCreationSettings, MockHandler)}
34  */
35 final class InvocationHandlerAdapter implements InvocationHandler {
36     private MockHandler handler;
37 
InvocationHandlerAdapter(MockHandler handler)38     InvocationHandlerAdapter(MockHandler handler) {
39         this.handler = handler;
40     }
41 
isEqualsMethod(Method method)42     private static boolean isEqualsMethod(Method method) {
43         return method.getName().equals("equals")
44                 && method.getParameterTypes().length == 1
45                 && method.getParameterTypes()[0] == Object.class;
46     }
47 
isHashCodeMethod(Method method)48     private static boolean isHashCodeMethod(Method method) {
49         return method.getName().equals("hashCode")
50                 && method.getParameterTypes().length == 0;
51     }
52 
53     /**
54      * Intercept a method call. Called <u>before</u> a method is called by the method entry hook.
55      *
56      * <p>This does the same as {@link #invoke(Object, Method, Object[])} but this handles methods
57      * that got and entry hook.
58      *
59      * @param mock mocked object
60      * @param method method that was called
61      * @param rawArgs arguments to the method
62      * @param superMethod The super method
63      *
64      * @return mocked result
65      * @throws Throwable An exception if thrown
66      */
interceptEntryHook(final Object mock, final Method method, final Object[] rawArgs, final SuperMethod superMethod)67     Object interceptEntryHook(final Object mock, final Method method, final Object[] rawArgs,
68                               final SuperMethod superMethod) throws Throwable {
69         // args can be null if the method invoked has no arguments, but Mockito expects a non-null
70         Object[] args = rawArgs;
71         if (rawArgs == null) {
72             args = new Object[0];
73         }
74 
75         return handler.handle(Mockito.framework().getInvocationFactory().createInvocation(mock,
76                 withSettings().build(mock.getClass()), method, new RealMethodBehavior() {
77                     @Override
78                     public Object call() throws Throwable {
79                         return superMethod.invoke();
80                     }
81                 }, args));
82     }
83 
84     /**
85      * Intercept a method call. Called <u>before</u> a method is called by the proxied method.
86      *
87      * <p>This does the same as {@link #interceptEntryHook(Object, Method, Object[], SuperMethod)}
88      * but this handles proxied methods. We only proxy abstract methods.
89      *
90      * @param proxy proxies object
91      * @param method method that was called
92      * @param rawArgs arguments to the method
93      *
94      * @return mocked result
95      * @throws Throwable An exception if thrown
96      */
97     @Override
98     public Object invoke(final Object proxy, final Method method, final Object[] rawArgs) throws
99             Throwable {
100         // args can be null if the method invoked has no arguments, but Mockito expects a non-null
101         Object[] args = rawArgs;
102         if (rawArgs == null) {
103             args = new Object[0];
104         }
105 
106         if (isEqualsMethod(method)) {
107             return proxy == args[0];
108         } else if (isHashCodeMethod(method)) {
109             return System.identityHashCode(proxy);
110         }
111 
112         return handler.handle(Mockito.framework().getInvocationFactory().createInvocation(proxy,
113                 withSettings().build(proxy.getClass().getSuperclass()), method,
114                 new RealMethodBehavior() {
115                     @Override
116                     public Object call() throws Throwable {
117                         return ProxyBuilder.callSuper(proxy, method, rawArgs);
118                     }
119                 }, args));
120     }
121 
122     /**
123      * Get the handler registered with this adapter.
124      *
125      * @return handler
126      */
127     MockHandler getHandler() {
128         return handler;
129     }
130 
131     /**
132      * Set a new handler for this adapter.
133      */
134     void setHandler(MockHandler handler) {
135         this.handler = handler;
136     }
137 
138     /**
139      * Interface used to describe a supermethod that can be called.
140      */
141     interface SuperMethod {
142         Object invoke() throws Throwable;
143     }
144 }
145