Clover Coverage Report - EasyMock 3.0
Coverage timestamp: sam. mai 8 2010 14:37:27 CEST
../../../img/srcFileCovDistChart10.png 0% of files have more coverage
73   310   33   6,08
16   182   0,45   6
12     2,75  
2    
15,8% of code in this file is excluded from these metrics.
 
  ClassProxyFactory       Line # 43 40 23,8% 16 0 100% 1.0
  ClassProxyFactory.MockMethodInterceptor       Line # 45 33 7% 17 0 100% 1.0
 
  (211)
 
1    /**
2    * Copyright 2001-2010 the original author or authors.
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 org.easymock.internal;
18   
19    import java.io.IOException;
20    import java.io.Serializable;
21    import java.lang.reflect.*;
22    import java.lang.reflect.InvocationHandler;
23    import java.util.Arrays;
24    import java.util.HashSet;
25    import java.util.List;
26    import java.util.Set;
27   
28    import net.sf.cglib.core.*;
29    import net.sf.cglib.proxy.*;
30   
31    import org.easymock.ConstructorArgs;
32   
33    /**
34    * Factory generating a mock for a class.
35    * <p>
36    * Note that this class is stateful
37    *
38    * @param <T>
39    * type of the proxy created
40    *
41    * @author Henri Tremblay
42    */
 
43    public class ClassProxyFactory<T> implements IProxyFactory<T> {
44   
 
45    public static class MockMethodInterceptor implements MethodInterceptor, Serializable {
46   
47    private static final long serialVersionUID = -9054190871232972342L;
48   
49    private final InvocationHandler handler;
50   
51    private transient Set<Method> mockedMethods;
52   
 
53  264 toggle public MockMethodInterceptor(final InvocationHandler handler) {
54  264 this.handler = handler;
55    }
56   
 
57  1110 toggle public Object intercept(final Object obj, final Method method, final Object[] args,
58    final MethodProxy proxy) throws Throwable {
59   
60    // Bridges should be called so they can forward to the real
61    // method
62  1110 if (method.isBridge()) {
63  6 final Method m = BridgeMethodResolver.findBridgedMethod(method);
64  6 return handler.invoke(obj, m, args);
65    }
66   
67    // We conveniently mock abstract methods be default
68  1104 if (Modifier.isAbstract(method.getModifiers())) {
69  20 return handler.invoke(obj, method, args);
70    }
71   
72    // Here I need to check if the fillInStackTrace was called by EasyMock inner code
73    // If yes, invoke super. Otherwise, just behave normally
74  1084 if (obj instanceof Throwable && method.getName().equals("fillInStackTrace")) {
75  10 final Exception e = new Exception();
76  10 final StackTraceElement[] elements = e.getStackTrace();
77    // ///CLOVER:OFF
78    if (elements.length > 2) {
79    // ///CLOVER:ON
80  10 final StackTraceElement element = elements[2];
81  10 if (element.getClassName().equals("org.easymock.internal.MockInvocationHandler")
82    && element.getMethodName().equals("invoke")) {
83  6 return proxy.invokeSuper(obj, args);
84    }
85    }
86    }
87   
88  1078 if (mockedMethods != null && !mockedMethods.contains(method)) {
89  356 return proxy.invokeSuper(obj, args);
90    }
91   
92  722 return handler.invoke(obj, method, args);
93    }
94   
 
95  260 toggle public InvocationHandler getHandler() {
96  260 return handler;
97    }
98   
 
99  120 toggle public void setMockedMethods(final Method... mockedMethods) {
100  120 this.mockedMethods = new HashSet<Method>(Arrays.asList(mockedMethods));
101    }
102   
 
103  16 toggle @SuppressWarnings("unchecked")
104    private void readObject(final java.io.ObjectInputStream stream) throws IOException,
105    ClassNotFoundException {
106  16 stream.defaultReadObject();
107  16 final Set<MethodSerializationWrapper> methods = (Set<MethodSerializationWrapper>) stream
108    .readObject();
109  16 if (methods == null) {
110  8 return;
111    }
112   
113  8 mockedMethods = new HashSet<Method>(methods.size());
114  8 for (final MethodSerializationWrapper m : methods) {
115  8 try {
116  8 mockedMethods.add(m.getMethod());
117    } catch (final NoSuchMethodException e) {
118    // ///CLOVER:OFF
119    throw new IOException(e.toString());
120    // ///CLOVER:ON
121    }
122    }
123    }
124   
 
125  16 toggle private void writeObject(final java.io.ObjectOutputStream stream) throws IOException {
126  16 stream.defaultWriteObject();
127   
128  16 if (mockedMethods == null) {
129  8 stream.writeObject(null);
130  8 return;
131    }
132   
133  8 final Set<MethodSerializationWrapper> methods = new HashSet<MethodSerializationWrapper>(
134    mockedMethods.size());
135  8 for (final Method m : mockedMethods) {
136  8 methods.add(new MethodSerializationWrapper(m));
137    }
138   
139  8 stream.writeObject(methods);
140    }
141    }
142   
143    // ///CLOVER:OFF (I don't know how to test it automatically yet)
144    private static final NamingPolicy ALLOWS_MOCKING_CLASSES_IN_SIGNED_PACKAGES = new DefaultNamingPolicy() {
 
145    toggle @Override
146    public String getClassName(final String prefix, final String source, final Object key,
147    final Predicate names) {
148    return "codegen." + super.getClassName(prefix, source, key, names);
149    }
150    };
151   
152    // ///CLOVER:ON
153   
 
154  264 toggle @SuppressWarnings("unchecked")
155    public T createProxy(final Class<T> toMock, final InvocationHandler handler) {
156   
157    // Dirty trick to fix ObjectMethodsFilter
158    // It will replace the equals, hashCode, toString methods it kept that
159    // are the ones
160    // from Object.class by the correct ones since they might have been
161    // overloaded
162    // in the mocked class.
163  264 try {
164  264 updateMethod(handler, toMock.getMethod("equals", new Class[] { Object.class }));
165  264 updateMethod(handler, toMock.getMethod("hashCode", new Class[0]));
166  264 updateMethod(handler, toMock.getMethod("toString", new Class[0]));
167    } catch (final NoSuchMethodException e) {
168    // ///CLOVER:OFF
169    throw new InternalError(
170    "We strangly failed to retrieve methods that always exist on an object...");
171    // ///CLOVER:ON
172    }
173   
174  264 final Enhancer enhancer = createEnhancer(toMock);
175   
176  264 final MethodInterceptor interceptor = new MockMethodInterceptor(handler);
177  264 enhancer.setCallbackType(interceptor.getClass());
178   
179  264 Class mockClass;
180  264 try {
181  264 mockClass = enhancer.createClass();
182    } catch (final CodeGenerationException e) {
183    // ///CLOVER:OFF (don't know how to test it automatically)
184    // Probably caused by a NoClassDefFoundError, let's try EasyMock class loader
185    // instead of the default one (which is the class to mock one
186    // This is required by Eclipse Plug-ins, the mock class loader doesn't see
187    // cglib most of the time. Using EasyMock class loader solves this
188    // See issue ID: 2994002
189    enhancer.setClassLoader(getClass().getClassLoader());
190    mockClass = enhancer.createClass();
191    // ///CLOVER:ON
192    }
193   
194  264 Enhancer.registerCallbacks(mockClass, new Callback[] { interceptor });
195   
196  264 if (ClassExtensionHelper.getCurrentConstructorArgs() != null) {
197    // Really instantiate the class
198  52 final ConstructorArgs args = ClassExtensionHelper.getCurrentConstructorArgs();
199  52 Constructor cstr;
200  52 try {
201    // Get the constructor with the same params
202  52 cstr = mockClass.getDeclaredConstructor(args.getConstructor().getParameterTypes());
203    } catch (final NoSuchMethodException e) {
204    // Shouldn't happen, constructor is checked when ConstructorArgs is instantiated
205    // ///CLOVER:OFF
206    throw new RuntimeException("Fail to find constructor for param types", e);
207    // ///CLOVER:ON
208    }
209  52 T mock;
210  52 try {
211  52 cstr.setAccessible(true); // So we can call a protected
212    // constructor
213  52 mock = (T) cstr.newInstance(args.getInitArgs());
214    } catch (final InstantiationException e) {
215    // ///CLOVER:OFF
216    throw new RuntimeException("Failed to instantiate mock calling constructor", e);
217    // ///CLOVER:ON
218    } catch (final IllegalAccessException e) {
219    // ///CLOVER:OFF
220    throw new RuntimeException("Failed to instantiate mock calling constructor", e);
221    // ///CLOVER:ON
222    } catch (final InvocationTargetException e) {
223  10 throw new RuntimeException(
224    "Failed to instantiate mock calling constructor: Exception in constructor", e
225    .getTargetException());
226    }
227  42 return mock;
228    } else {
229    // Do not call any constructor
230   
231  212 Factory mock;
232  212 try {
233  212 mock = (Factory) ClassInstantiatorFactory.getInstantiator().newInstance(mockClass);
234    } catch (final InstantiationException e) {
235    // ///CLOVER:OFF
236    throw new RuntimeException("Fail to instantiate mock for " + toMock + " on "
237    + ClassInstantiatorFactory.getJVM() + " JVM");
238    // ///CLOVER:ON
239    }
240   
241    // This call is required. CGlib has some "magic code" making sure a
242    // callback is used by only one instance of a given class. So only
243    // the
244    // instance created right after registering the callback will get
245    // it.
246    // However, this is done in the constructor which I'm bypassing to
247    // allow class instantiation without calling a constructor.
248    // Fortunately, the "magic code" is also called in getCallback which
249    // is
250    // why I'm calling it here mock.getCallback(0);
251  208 mock.getCallback(0);
252   
253  208 return (T) mock;
254    }
255    }
256   
 
257  264 toggle private Enhancer createEnhancer(final Class<T> toMock) {
258    // Create the mock
259  264 final Enhancer enhancer = new Enhancer() {
260    /**
261    * Filter all private constructors but do not check that there are
262    * some left
263    */
 
264  62 toggle @SuppressWarnings("unchecked")
265    @Override
266    protected void filterConstructors(final Class sc, final List constructors) {
267  62 CollectionUtils.filter(constructors, new VisibilityPredicate(sc, true));
268    }
269    };
270  264 enhancer.setSuperclass(toMock);
271   
272    // ///CLOVER:OFF (I don't know how to test it automatically yet)
273    // See issue ID: 2994002
274    if (toMock.getSigners() != null) {
275    enhancer.setNamingPolicy(ALLOWS_MOCKING_CLASSES_IN_SIGNED_PACKAGES);
276    }
277    // ///CLOVER:ON
278   
279  264 return enhancer;
280    }
281   
 
282  792 toggle private void updateMethod(final InvocationHandler objectMethodsFilter, final Method correctMethod) {
283  792 final Field methodField = retrieveField(ObjectMethodsFilter.class, correctMethod.getName() + "Method");
284  792 updateField(objectMethodsFilter, correctMethod, methodField);
285    }
286   
 
287  792 toggle private Field retrieveField(final Class<?> clazz, final String field) {
288  792 try {
289  792 return clazz.getDeclaredField(field);
290    } catch (final NoSuchFieldException e) {
291    // ///CLOVER:OFF
292    throw new InternalError("There must be some refactoring because the " + field
293    + " field was there...");
294    // ///CLOVER:ON
295    }
296    }
297   
 
298  792 toggle private void updateField(final Object instance, final Object value, final Field field) {
299  792 final boolean accessible = field.isAccessible();
300  792 field.setAccessible(true);
301  792 try {
302  792 field.set(instance, value);
303    } catch (final IllegalAccessException e) {
304    // ///CLOVER:OFF
305    throw new InternalError("Should be accessible since we set it ourselves");
306    // ///CLOVER:ON
307    }
308  792 field.setAccessible(accessible);
309    }
310    }